From 9c4a818423084ab26d292a07772fadd393d2d693 Mon Sep 17 00:00:00 2001 From: Sergio Martin Date: Fri, 22 Mar 2024 16:31:22 +0100 Subject: [PATCH] Replace QuickNES core with QuickerNES (squashed PR #3839) resolves #3848 --- .gitignore | 2 + .gitmodules | 3 + Assets/dll/libquicknes.dll | Bin 376715 -> 152576 bytes Assets/dll/libquicknes.dll.so.0.7.0 | Bin 282824 -> 0 bytes Assets/dll/libquicknes.so | Bin 0 -> 386224 bytes Dist/Package.sh | 2 +- README.md | 2 +- contributing.md | 6 +- quicknes/bizinterface.cpp | 285 ++-- quicknes/core | 1 + quicknes/fex/Data_Reader.cpp | 775 ---------- quicknes/fex/Data_Reader.h | 265 ---- quicknes/fex/blargg_common.cpp | 51 - quicknes/fex/blargg_common.h | 217 --- quicknes/fex/blargg_config.h | 37 - quicknes/fex/blargg_endian.h | 185 --- quicknes/fex/blargg_errors.cpp | 113 -- quicknes/fex/blargg_errors.h | 80 -- quicknes/fex/blargg_source.h | 121 -- quicknes/make/.gitignore | 2 + quicknes/make/Makefile | 66 +- quicknes/msvc/.gitignore | 4 + quicknes/msvc/libquicknes.sln | 2 +- quicknes/msvc/libquicknes.vcxproj | 196 +-- quicknes/msvc/libquicknes.vcxproj.filters | 267 ---- quicknes/nes_emu/Blip_Buffer.cpp | 412 ------ quicknes/nes_emu/Blip_Buffer.h | 354 ----- quicknes/nes_emu/Blip_Synth.h | 208 --- quicknes/nes_emu/Effects_Buffer.cpp | 518 ------- quicknes/nes_emu/Effects_Buffer.h | 93 -- quicknes/nes_emu/Mapper_Fme7.cpp | 213 --- quicknes/nes_emu/Mapper_Mmc5.cpp | 155 -- quicknes/nes_emu/Mapper_Namco106.cpp | 216 --- quicknes/nes_emu/Mapper_Vrc6.cpp | 262 ---- quicknes/nes_emu/Mmc24.cpp | 117 -- quicknes/nes_emu/Mmc24.h | 7 - quicknes/nes_emu/Multi_Buffer.cpp | 213 --- quicknes/nes_emu/Multi_Buffer.h | 175 --- quicknes/nes_emu/Nes_Apu.cpp | 380 ----- quicknes/nes_emu/Nes_Apu.h | 177 --- quicknes/nes_emu/Nes_Buffer.cpp | 201 --- quicknes/nes_emu/Nes_Buffer.h | 68 - quicknes/nes_emu/Nes_Cart.cpp | 268 ---- quicknes/nes_emu/Nes_Cart.h | 93 -- quicknes/nes_emu/Nes_Core.cpp | 572 -------- quicknes/nes_emu/Nes_Core.h | 117 -- quicknes/nes_emu/Nes_Cpu.cpp | 1268 ----------------- quicknes/nes_emu/Nes_Cpu.h | 132 -- quicknes/nes_emu/Nes_Effects_Buffer.cpp | 84 -- quicknes/nes_emu/Nes_Effects_Buffer.h | 38 - quicknes/nes_emu/Nes_Emu.cpp | 506 ------- quicknes/nes_emu/Nes_Emu.h | 284 ---- quicknes/nes_emu/Nes_File.cpp | 226 --- quicknes/nes_emu/Nes_File.h | 128 -- quicknes/nes_emu/Nes_Fme7_Apu.cpp | 120 -- quicknes/nes_emu/Nes_Fme7_Apu.h | 135 -- quicknes/nes_emu/Nes_Mapper.cpp | 222 --- quicknes/nes_emu/Nes_Mapper.h | 235 --- quicknes/nes_emu/Nes_Mmc1.cpp | 123 -- quicknes/nes_emu/Nes_Mmc3.cpp | 252 ---- quicknes/nes_emu/Nes_Namco_Apu.cpp | 149 -- quicknes/nes_emu/Nes_Namco_Apu.h | 104 -- quicknes/nes_emu/Nes_Oscs.cpp | 545 ------- quicknes/nes_emu/Nes_Oscs.h | 150 -- quicknes/nes_emu/Nes_Ppu.cpp | 669 --------- quicknes/nes_emu/Nes_Ppu.h | 141 -- quicknes/nes_emu/Nes_Ppu_Bg.h | 68 - quicknes/nes_emu/Nes_Ppu_Impl.cpp | 520 ------- quicknes/nes_emu/Nes_Ppu_Impl.h | 230 --- quicknes/nes_emu/Nes_Ppu_Rendering.cpp | 496 ------- quicknes/nes_emu/Nes_Ppu_Rendering.h | 63 - quicknes/nes_emu/Nes_Ppu_Sprites.h | 132 -- quicknes/nes_emu/Nes_State.cpp | 293 ---- quicknes/nes_emu/Nes_State.h | 142 -- quicknes/nes_emu/Nes_Vrc6_Apu.cpp | 217 --- quicknes/nes_emu/Nes_Vrc6_Apu.h | 99 -- quicknes/nes_emu/abstract_file.cpp | 308 ---- quicknes/nes_emu/abstract_file.h | 162 --- quicknes/nes_emu/apu_state.cpp | 132 -- quicknes/nes_emu/apu_state.h | 79 - quicknes/nes_emu/blargg_common.h | 164 --- quicknes/nes_emu/blargg_config.h | 33 - quicknes/nes_emu/blargg_endian.h | 159 --- quicknes/nes_emu/blargg_source.h | 76 - quicknes/nes_emu/misc_mappers.cpp | 348 ----- quicknes/nes_emu/nes_cpu_io.h | 144 -- quicknes/nes_emu/nes_data.cpp | 75 - quicknes/nes_emu/nes_data.h | 174 --- quicknes/nes_emu/nes_mappers.cpp | 117 -- quicknes/nes_emu/nes_ntsc.h | 199 --- quicknes/nes_emu/nes_ntsc_impl.h | 520 ------- quicknes/nes_emu/nes_util.cpp | 211 --- quicknes/nes_emu/nes_util.h | 90 -- src/BizHawk.Client.EmuHawk/MainForm.Events.cs | 2 +- .../images/QuickNes.ico | Bin 9062 -> 1150 bytes .../images/QuickNes.png | Bin 592 -> 514 bytes .../Consoles/Nintendo/QuickNES/LibQuickNES.cs | 11 +- .../Nintendo/QuickNES/QuickNES.ISettable.cs | 30 +- .../Consoles/Nintendo/QuickNES/QuickNES.cs | 178 ++- src/BizHawk.Emulation.Cores/CoreNames.cs | 2 +- .../vpads_schemata/NesSchema.cs | 24 +- 101 files changed, 465 insertions(+), 18045 deletions(-) delete mode 100644 Assets/dll/libquicknes.dll.so.0.7.0 create mode 100755 Assets/dll/libquicknes.so mode change 100755 => 100644 Dist/Package.sh create mode 160000 quicknes/core delete mode 100644 quicknes/fex/Data_Reader.cpp delete mode 100644 quicknes/fex/Data_Reader.h delete mode 100644 quicknes/fex/blargg_common.cpp delete mode 100644 quicknes/fex/blargg_common.h delete mode 100644 quicknes/fex/blargg_config.h delete mode 100644 quicknes/fex/blargg_endian.h delete mode 100644 quicknes/fex/blargg_errors.cpp delete mode 100644 quicknes/fex/blargg_errors.h delete mode 100644 quicknes/fex/blargg_source.h create mode 100644 quicknes/make/.gitignore create mode 100644 quicknes/msvc/.gitignore delete mode 100644 quicknes/msvc/libquicknes.vcxproj.filters delete mode 100644 quicknes/nes_emu/Blip_Buffer.cpp delete mode 100644 quicknes/nes_emu/Blip_Buffer.h delete mode 100644 quicknes/nes_emu/Blip_Synth.h delete mode 100644 quicknes/nes_emu/Effects_Buffer.cpp delete mode 100644 quicknes/nes_emu/Effects_Buffer.h delete mode 100644 quicknes/nes_emu/Mapper_Fme7.cpp delete mode 100644 quicknes/nes_emu/Mapper_Mmc5.cpp delete mode 100644 quicknes/nes_emu/Mapper_Namco106.cpp delete mode 100644 quicknes/nes_emu/Mapper_Vrc6.cpp delete mode 100644 quicknes/nes_emu/Mmc24.cpp delete mode 100644 quicknes/nes_emu/Mmc24.h delete mode 100644 quicknes/nes_emu/Multi_Buffer.cpp delete mode 100644 quicknes/nes_emu/Multi_Buffer.h delete mode 100644 quicknes/nes_emu/Nes_Apu.cpp delete mode 100644 quicknes/nes_emu/Nes_Apu.h delete mode 100644 quicknes/nes_emu/Nes_Buffer.cpp delete mode 100644 quicknes/nes_emu/Nes_Buffer.h delete mode 100644 quicknes/nes_emu/Nes_Cart.cpp delete mode 100644 quicknes/nes_emu/Nes_Cart.h delete mode 100644 quicknes/nes_emu/Nes_Core.cpp delete mode 100644 quicknes/nes_emu/Nes_Core.h delete mode 100644 quicknes/nes_emu/Nes_Cpu.cpp delete mode 100644 quicknes/nes_emu/Nes_Cpu.h delete mode 100644 quicknes/nes_emu/Nes_Effects_Buffer.cpp delete mode 100644 quicknes/nes_emu/Nes_Effects_Buffer.h delete mode 100644 quicknes/nes_emu/Nes_Emu.cpp delete mode 100644 quicknes/nes_emu/Nes_Emu.h delete mode 100644 quicknes/nes_emu/Nes_File.cpp delete mode 100644 quicknes/nes_emu/Nes_File.h delete mode 100644 quicknes/nes_emu/Nes_Fme7_Apu.cpp delete mode 100644 quicknes/nes_emu/Nes_Fme7_Apu.h delete mode 100644 quicknes/nes_emu/Nes_Mapper.cpp delete mode 100644 quicknes/nes_emu/Nes_Mapper.h delete mode 100644 quicknes/nes_emu/Nes_Mmc1.cpp delete mode 100644 quicknes/nes_emu/Nes_Mmc3.cpp delete mode 100644 quicknes/nes_emu/Nes_Namco_Apu.cpp delete mode 100644 quicknes/nes_emu/Nes_Namco_Apu.h delete mode 100644 quicknes/nes_emu/Nes_Oscs.cpp delete mode 100644 quicknes/nes_emu/Nes_Oscs.h delete mode 100644 quicknes/nes_emu/Nes_Ppu.cpp delete mode 100644 quicknes/nes_emu/Nes_Ppu.h delete mode 100644 quicknes/nes_emu/Nes_Ppu_Bg.h delete mode 100644 quicknes/nes_emu/Nes_Ppu_Impl.cpp delete mode 100644 quicknes/nes_emu/Nes_Ppu_Impl.h delete mode 100644 quicknes/nes_emu/Nes_Ppu_Rendering.cpp delete mode 100644 quicknes/nes_emu/Nes_Ppu_Rendering.h delete mode 100644 quicknes/nes_emu/Nes_Ppu_Sprites.h delete mode 100644 quicknes/nes_emu/Nes_State.cpp delete mode 100644 quicknes/nes_emu/Nes_State.h delete mode 100644 quicknes/nes_emu/Nes_Vrc6_Apu.cpp delete mode 100644 quicknes/nes_emu/Nes_Vrc6_Apu.h delete mode 100644 quicknes/nes_emu/abstract_file.cpp delete mode 100644 quicknes/nes_emu/abstract_file.h delete mode 100644 quicknes/nes_emu/apu_state.cpp delete mode 100644 quicknes/nes_emu/apu_state.h delete mode 100644 quicknes/nes_emu/blargg_common.h delete mode 100644 quicknes/nes_emu/blargg_config.h delete mode 100644 quicknes/nes_emu/blargg_endian.h delete mode 100644 quicknes/nes_emu/blargg_source.h delete mode 100644 quicknes/nes_emu/misc_mappers.cpp delete mode 100644 quicknes/nes_emu/nes_cpu_io.h delete mode 100644 quicknes/nes_emu/nes_data.cpp delete mode 100644 quicknes/nes_emu/nes_data.h delete mode 100644 quicknes/nes_emu/nes_mappers.cpp delete mode 100644 quicknes/nes_emu/nes_ntsc.h delete mode 100644 quicknes/nes_emu/nes_ntsc_impl.h delete mode 100644 quicknes/nes_emu/nes_util.cpp delete mode 100644 quicknes/nes_emu/nes_util.h diff --git a/.gitignore b/.gitignore index 0922affde8..451d48dd25 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ /test_output **/ipch/** /Assets/dll/*.wbx +Backup/ +UpgradeLog.htm *.ilk *.il diff --git a/.gitmodules b/.gitmodules index 987d792c0f..859dc7d313 100644 --- a/.gitmodules +++ b/.gitmodules @@ -70,3 +70,6 @@ [submodule "ExternalProjects/SDL2/libusb"] path = ExternalProjects/SDL2/libusb url = https://github.com/libusb/libusb.git +[submodule "quicknes/core"] + path = quicknes/core + url = https://github.com/SergioMartin86/quickerNES.git diff --git a/Assets/dll/libquicknes.dll b/Assets/dll/libquicknes.dll index 3e35c8bc7c9889716a3ca7f08f6fbddaf48f931a..41147817b3ea5da7909aee459acec2b4eddca993 100644 GIT binary patch literal 152576 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ȿ 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 OdcLqda~7C{I8}d3pk)JV86k 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ð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

% z*zPshohCY_2Zia^Vt1R^mHE4}E2{&_E?IR02?!Von1D^H55kxAfv|6rc~QAnjB&j( zRug_CuUjchpMuF|Wg^Q7_$Vs~MB+vz#Ucq@mJKTQ$}TO8W!<3Zm0amsUeqW7F8{_1 zLga{=rdtP_*&>6T6vb+!RqX!aFxy>j*d0RGX823d6MNs&M{pjSYD=6ikG)!de*B2d zT(VWT{-;6CAHQ}Pvs3>%y{LcS%zUfqqao4?Oh`|nD(lO~eW=FCMh(7L#(Ci;tvhXm z(|3}~`=GSpzA^l^@@6XNiR00s0W?((bJ z0<=`gw-3FUyHFZ4SqY%|nJdlQ+B98rn>2HqGIR|Mou%8-99=Vp`q%A2r>?oqjpniO zx~7=i@Ak(MU2{_@nrF*(&GQ~YM8CFL*RQY9^_!OKdS9)sU%E=y-&d#WA6%#F9|`FC z$9CxYCmVGAA9w5eryF(svrW4G`4(Nj_7HkqG!BHOm0d&Ds|5n2V~t3s&YZdAdiGgr z>0ZwZr8BM8;AfMT zyGE+)pwE?gwM7PM5}CM9n-Zm>_%}(%Q{er)=~XGso=s|1nudC@j5W%Nssd%xB1QN< zRfP3i-l}F9dcSL_T1tYQoCzt5<#C^?Q4(wB@;%}K88^s!xj-fhRHVj6H*9=}AVdjB zo9B`#ONnXed#8AL|ik|EV`#w1JivL_Yr zD=vR_TGMlbB2E}$Bh^q%HBoFXVrOa78k&{J_G8fQIMUuGwAgg^hsd)tdAEpudY0h2Um@cmrF4j-^W3jMknymK(EK8r!eiQ)WBIdT$_p?Wu5{j>@v% zJa2mOO;hKL8ZxHe$VsK8Q|Al`8-h{hSvjMo&Yd`9+O(;~WpgLy&EqA?@)7p4kFXo% zU%w;0h%(RWGp20noT;--K}DNqjhsEpY#JNi-XrJDD4T9_9+RFvW2VoTI;+fV!IXA+ z?Gj9l&q#E4tW6r&B_;W^lw@n#iT2c@Qvzwu)2uygXC|_kYGuvJ!FrNMtJPlDg4%+( z!*M^yA4>SH(>{w97oU*Wp<|~`Nu9fNO+F#TVzG8R@ubw$lTYcMmUilCr=Q;Aj5E(V z%ht2!+2^FEpL^bU=VxSQUU1=sy)L@w;!7^E+p~K2?vtI}w{O4x{c~~#Tzctcmkk_v z`Q=v(8su=~=H}%M9-N;)Wa!Xg!<v)f zD~xl*of#L@_G%w!8;RpR#Pl~>q2|>3Y4^lUiW?Z08P_H5fcCDoU3*TuU#rq?)h27B zw82_$Eum+Jj!hML=bx0Ddcy7>&N;DLx1^+>|7triYf^HjRZ}LN-@E6T9Y4DzE+am! zqxR(9g9m#~I-}EF72P_W)AQ`^Pt1wy-nZMinq%&qxEJCqmQ{baGQN9#dZ$B&&OY(x zbGmh$T9$T7T>N_-Ub{abeo}F=wk*)+hn{mz(CXhG+c7#DSdf-QYfTmdS)h|67UT#D zIcNgeYg#%g1Jw&OB8x5?@HO%vL3>at|tm{1n zHX7*2*ZEolwIPnt^U7?qr`cvsojH5XZ?*8#sN2&T2Oa*?Ki?fW*8KSsp8M{!$L5!ur0W9{4L&Vw zKImcHpIjYJ$kjBh@yc}@4gVSjiS*l^7Kip{iNAZ$g^N%5=&c{WHSAdpoV7`}JK51k zJ^Kyb?NEGRPCz$Lb?D(WvlU35bcX`1&PeY7-CgimgVzkcv*YH=yL|X$>E8x9KbxQW z%&^Z4{~HW@_QAS8wt$Wex%y|AlMQ>aW)qC z+&F(whu^Q5wt4#g^Ncew6UJ>tMFzeCEu(%qBkq46=u~1y;(_( zF)Q{mX=Ww)X;vqqH!G=WW_1dBvyxh9R;QsiE2*JobtZbVvY|Jtv(cNC)NHdl54~AQ ztv4%{^EI=Q7Qw78MsHSj^k&r?y;(_1Vpjdoo0YUOW_2lgvl@urtfYXOl>@z5<)JsL zeDr2D6untF(VNw9^k!9n-mFHUH!C-Kv$_hsSrwu;tFh?KYCL+gx)!}zO+ar}H=sAG zBJ^f88NFFeL2p*m(3@2Wdb9csdb65=-mGS!H>*<9{%WMCIi+LUAiQ$TsdjXYu|?;Q-} z2SCbwX9KygNZH|2r9wU`iG`?t61KRPu*E$OZTpr@2I}eXjB=o_edwW=jC73^Jx^~3 ze>|wjBYU5C?u-z|OaAI;CY%OgR~woLr-!E;a8-wT!bs~vUv{d*Px`hiZ1o+l&`#Vq zRMU3ky|`u9aK|OVkQ-Hk@}O!_J5Y@m2SYWeI<}UUdj5OmxGNZH!jGbV!ygBB%qliq zvzff7R|P{);QxQC)>H;Vg{Xu7h96t8V^*XM#a!@tsSD|XF(R+iT zB$N$Rg5HBtnb*#r=<$C4nQDU4J3D*g+_Fh!Qzw=UX~&J3igrJsY|`|y*%QmN?DoF@ zo)6LPb7oDNSv-4UpX~DTqd1SY-+N|O-+o82smC(few+V&j^ZlXe(zi66!(v^d8B#P zam=Gbig7<}=G0^PpIugL&l+Hl3c)P>*!fsLJ^%XjKB|kD@JU~^{Ejp~x?d8LX#4$C z{$<&t{fv%rv@Y6xe|>^%&pK-9iMHQ+=FH+~t4Eq2Q~tO=M*a=ZqtiRPU!)pD`5!UQ zK8|_Tam?+*yi^B$1y*4{Idr1Ii`C`;IYkfj-CFjoa4Cf+ZMm*(h*y}jPfyY zRJfVw<0_b(HDpL6da=%<%V%%9o}m-_XP0O7%R==o@6+2=Fg>epZ^M)J@;58HE&j0q zMA^^EHvG@MW$LKuvnI6@xhVTa`LPc;cK+E199zEj~A8mHT)&7NZ`o;_virM7eD$?R0q z*z2gCFba1y>dz=O3Gisz)653PqT*1inBbaKA(K9{;`bOKvs%s1R?JFH!!bWvtscI% z+`t+Gs|_R`^60uyNX=F~nkLJa#XbM#AXaKYU+l2%x=H@#p=2&9b8&f2)3J- z@cVv6gX*qeljk7r!aybEhC*qmQq(H+FHH}H&b~GjT5)wKv}=4Qw5NcdZga)aqYJn} z(H#mcyfG9?{xw^7KM42xcz?wm3U$0L6e=N% zD$MKWU=Q~`{F;yQp}zz(H~x$Jj|gkcO`M;N`;Sp&xcA_`7Z-UrZ_&elV_qnfIiEQF z7Wsg?!S3a{`|t2m;{PIU-$C7k`%mC(*pu+PgA#t(tWfCTnW4}!%;Lc_XXx$^61KSi zxiA!}BYhq5;}GFy6Th|SXJCH<%89B+Kkql8(36Be7A5h&7!>#a#QhH5TM568`2HCs z;Xe*a_--&C)mp}HuTV=-;(jV9?yn@ha--W9qq%;D@UKVjLJphfl5ePy*js;FD6|rF zJL+VVxZewkdnfi^!u=lHpHqbVrs(0H14{V4i;>ObQ0O3LV*gPCE%-YIcjc(xqu#`y z$%a`aC}F%ty00al%E0xQALc-(=TTyo0}jXCY}7K;1h|%I*h4*vIuF$iw_Q>=-`82P{Z zX7Unu*W&&;@Fm<2C<}!$z#`1PEe(bC^WFz_8t$LIArz{{-U}0TIiwK(+tJ@WA{2Ux z^ktHNwS;va`h&!+3)~skg+h-^2!+l<&ApzoIy)4agOczLin}R<;X=QJ_@6BDM8(eygV8T}=w4RDRfP~5+Z3?%Fl}F%cjqsWt%f~QgO-DDYnT|X^5xN63;4|e&f8^^XA&- zPMtG-(v0czCy71rKJ3r*S^tSQQ>I3HH14Y0k=kJARoY+|Z?+7k+f&OXO`b7zZl>nG zYMAY+(aP91R$kn>1!8?PYTm4yX3f5JmL}6(G;?Op)QZ(#h(5yh)h5jrme=0$CtE9? zJ!AHqiBm}8%(>c4S@zxoF6m=;YvBpL#>zqOUi`_<=$(~m*7c6k^-;Qhlgu{y>fB`Q zYKcSNtlpYy&eWL`hh^Ebv_e@xDjY1U9@;FXP+8h2(sEH&?;Pz$6=PX3(W2&W8U5{< zS}~HCGrd@a(?Kc^NFZ5# zvop0R)93c?!!pmrDU)W;)8>`qea7@zw@jT=Hg!rQ4w9N1*+wvZ%8j#U^It0~&O&Y~ zH;8R-d%sLgt-YzGH|^$mlgi5G%nqlkcecIlH6j?6HTX8Qfx3w*L9IhIqS9XBeLe4} zz$V^Nb*NRSTGVosbt8xO8CVT^Q00bR%tV#O=t~TJ5jY;@MmbRqR1PZaPS9@XGe8?E zEymnz_y6hpQNl?w@fz=kh+~V9&L#sJ4V3uoMm3o9<~Z(v51`he>QJjtX7gI~%S~L3 z>D_Ga-+izCAG-f{!x4L)|1hj_!+xoOCI6xO|0Hf=wWQl^(^gKSgX*IpI1@}bk2ZO;Wb;0{;g4=Hme*c?baMqi_ zyGr*zcFrrC_#w1*{TKEjpWWqr>+hpSkJg=FzrHcJ>%Cy}j^Hm_g27k!2jATG<3~?@ z@#XyuEe~$|*VAjhfAfU{->+M@PPad2%xJCq%{J}cVVb{0yK%mj$=AqYuKqY%OQZQ5 zq`p31#GCPKT(mS8 zKlgL+sYAiX_XVHa9DMrs!L_&lymiJGZ_Rq|y}Mp}cjX(ezaEMIh~GH0izjQR++|C+ zBUc-Lm3ErDt9BWYzmzeF`qVsz58&sAqpwx3nXgTK>Wm=`?p|-)W_##&>CT$|T3Mks zv`jn6uW1iy>|fIEk}uHu^LB7UpS8`C9}GTlN3j0pJs-{EYSxQheo9-iO&i~+^*W%X zHVx|j{v(n2W54FBx1apDaoyXWzV`ZS@9kXm?ykGGH5RY=$CZ!$IL3dtr1BSU)%VXv z;y-3gf35p9n)cfaex<05zEN@oRE7IF&1&aVpQe=+fl9k*)_ z|6ZH+qIT&P?d(mLocr{WNc^$?=qsC^t83Wy)LXkBf9bO)mp48==l!)~HgC!M%Uk)6 zyf^;dcjo`@wZ|gyA2)8HcG@31#Bb}RZ5gTkexi26_1dY!Qnd8*G;M&I+73ET%aSkX z`t#63yS3rLi!TgL>K0tm;m4Jo|L}N^;R^?7)IFlg2R^u z%gchRuM6%T75w(9;6J84)lhlO6Hi_|>yv~s`_QWOHqi(HyvUX{a z+5mC>DQX`Gr(oFQwVuvMd>*ZODzWdoy-ztjRr|3ZVdegP*^0kcXHEp`4 zO}#+7YEi`g*RTCB`nSQ`uL(XkDEPNN!GHDN^4;)hAC#p1v09t^xHjyWbM1dF@BP60 zk@#c(=~cmvj|AVlH~7Vp;J5dD_KyeF?|ov$+t2u3e#yUT9zz99Iz`}rNkW1p@_y1iC& zy{w(F?c@%dOOl>_H4=a9SJWR`wkG(eKL^+U;k&I*y|d++r`Nr_laV`;fD>69DMVE#t)Zo{NS}`rk;4s z)+;IQ^n}Y+pF~7i?6Dp^9NNKqY3OjM1@wSPsjLchZ4Djjh0n>Yp$t14B)|=&kE(hzt z)nEV&fDPa-&~_TfAcA)AFz5iSEP8psbg%}@0c*hmun{Z*Tfj0I@7pOf%JV6II1*`!*U?aFp z^beCCq6hbY+9Q-(&fk$8ed;A*hp&&UU?eVY7ZJW%@#{)2U34Om)7K7cjg4$%G_asb_6;yKv+ zE9n9qYj6j;!5ZPgm4Yu2Kd=GZ30hyIUJ9-yT+j|mKT-)cITfh~f{{;7-eGhU0i$0}%W)kj~qz`m7Q{TYauaWx&q;o&=5&~YMr+`txa1E`&ZJit0|ALvO9h7JjTaxj$KhxCCq z&<19McF+krzzLumECq|eDzFq>4tl`VU=0`mYr$P$9oPg0z{6kzXw62>U^>_W=78EM z!B7Ec1&cr%SO(g`YS00$0Nr36SOjhbOTpcs2Rs1QfHKLc1yjK~Far#L1HlH+4K{*P zz!tC^)VfoCKr2`a+Q2oS9ozvrz&)TFYypeF#D0Vi%B0o<_5y1_2UrUhf^}dC7yuW5 z4PXt}2(AQMz;&ROM*f3Vuo1L@2SGcS)Sqy{9$*n@2TQ?xFaVARTfiBh^;Gg7ECQDa z5AFaPPb2=Ib~^C~Yr(`E_#VU`Yyo?L+8M+jw1S184J-le-~!MA)_`tsC0GQm153f3 zpa*OOYrunGEtoU_IeK4=4Lz-+J95*a$8MTfo(zb{6Fqw1T@p8`uQe!NZ^f zv|dK|U^-X?=76PO0q6mXzyMecYBtI(Xb0;+H@Fq71rLA$Fliw6dJ=EY4rYUH&o7gn#T` zmR}S#faPEdxD2#jLOz2J3;rfbtAFE~Pw!*2`#5^58)? z=pINr0v26Py#O79D5s#uLHM9Om+-;5Jo0rg?gkToP|HW&U;wNF9Yd&3pl2xY7yU5Y zi{42)mXH4~!UgTaiMQy%LeMsX`Uz?UlsC`=ZUyZlaSxWdaX$q8SkehLOduaYYZ3W4 zlz2@h9bn58;tvL<5`VB}8ulp zf0FtQHh>PWbS32gwEuy271aJnxcwX!d&oW(l5B4_G%37z!hN88^o&+Y@j{~zC(EjJz&z+ zloK!=YyfjW?On`@j|}-_iw7;``cl*h}S` z1z2=4`x?MnunM%D!afYp0j?8$cfNgrrDw2jWCHfi#2#qt$u}=B03HPG=dh>ddi*;N zbFd|Y?_HqdLcVw1Kze$?gSI~GvjJQB6OW0c>u%x)Hu(4s1~!7L!4@z8YD?J116si* z&<*AkAy2RxYy@|Mc0cp1`K=+G$w*!m7WuOOK12%wrz`#1vKaF_tJ=g&jZ6#koc~W)I z;^tqa#g$v*PEYQXR1>HFrmzQQj>f~GqOQE8Scj&h4Yzc?HOZq5I_=U6`lO#NPQ`sb zs(k<9kW~od5uPHXmBRDIBJzlxGL$o+HS{Lo$Rk`eDzQUrsDltZ!mW^(7_JWLaALH* zt#F6n(!@TGxZRD4_3wZf;$J609l5;%7ZR|{7F=Px_@RFq``@Q;j9yXd%=l+X3haO15V0{JQ=7$!@u_u&z}A7#qEaM%6jmAi35+=J0Sjbi!M7F<}2VjD_gp~RJi4E8<3Se*{BS- z{F7Qk4~l<7Q>^~@f|Rr+3Arh@MTxm78I>Ip&PnK!V#`fQ%S*B5r6lDeB#}uG_E(?M z8Y(7ikws;E!YOeiaR}xTClBU_(^^AvDWW{WErWBO7G0iK!4<-trfgB}zzuLkaNDxb zTq$yPiJY%Q&Mzc{#XNM&HfM^hGGS4?${RTu{_u#_(CH#8GGbA@Q``*2P3Oe08;k~` zdSItzbZh8I$pF%PPyDcyG=D-~ifxIcc~OVl6nkaIgzr0aNy*4hvE_x+PWGvEOu&87 z4P4QHb9p3frEp8&x+_NKs^FHxC7ZbAaLeE}nz+?)j)|>uPlS3H!f64x32vZaxXD7`|B^^qjwi44Q>?>MWC=&(f|SPfSO_a^4@h`kkXcFvHwSTTA(QwLW7w}^6V%5N11 zmEUb~NH~K88mXgV==Cy>NJtxX2!EE%YzafHkorZB0cCRx9aHlFpub%<9 z18_G9*OreoNAcH%E1UE)P1wukd(!CKj-j zoSPzM>oD7eJ69x}@<=$lFiTp{Hde{NP7_={+*4v-wPUpVi=_XmOpL!zNs3-eFSDt1 zt~;=sCw5hz)#kSyv%{F3C}up8hJ3grzR@ZB!=#V9Ma88TW<{6{#B7vNu2lQED1rQ` zOiUO?p_b7AAv-C{(&yA*f5M$cn@BwGiKh(trHxsVm>=nL;up~65cg5xYAsb&#iarF z$)48GIq3Dc>+%#C9Kg(m8BILxJYZR$3!2~rr}{Bc2)b}OtF@dBJslxzX1M3 zqpmFxhlU&zI>tpjNyqL4?3PxvhK5LZZDn&rifxv%#~?_>xe9yBu@^3zmGM_A`H3I3 zn61EU@)3T-)A;HmDXAMfu(uC;En<%+2ek(-a93-HsUr=yj9FA)g6tP1(v(zo7@A_g zF}f!iwq@JSHznLOI^%t~l|0nrK3K*H#GR&exEjEzy5qp!0qljxqT;p?t_kkaBixRQ za;xrAscdtr@*lT{ar>;eZOeZ(<}l^|Y(4*nhx1>GkLs@*a6bb-Reg$*Iq@FR7VksY zD>H>Hd7DfkO5yU2u+3$4P9$gbHX{eS%S`Si4+`LF;LPLgBDfWB7l@xc5>6T1IyjZ5 zSIL-yvE(8t z87I5(Zztc|UlqT}KY#q_=y7uVqvm1qP{}uucQx*&cw0k18|}nBGLAEkHHXPqb685l zNeL^6j3kiyA4#ALIaPUVz@Hv`*O&Z|$A;Pmmj?Hu$dD>i$vD=XFac3nse&VvY?3UR zw#MHYIvFeSNIC8S7l6B82-317{z}QG;q)!j)!2`AOTx*=Ud}yys}*}ZlE>rW^5Mox zn5rBy_Nx@P33k1eklL@0+p4fzzO*$YSBFVB=5brhm?l5P5&yS#hi$Zg5|;-2+EBw; z`Mk>`+&(nx;0hI^b8HbZB%pa8^hg%n#Zjzld>bu(Bh!1N4*RA5T++cAnOoj*WUlG+s3QC*SY`Ancq3^mgR6#nkau~;qpIOj z|I`}#o6!z0m3FiJv2*<6M+Qb&Oh`DQh~Te;w-3LUuWqY1BGW@~%ixsE+wyZ%ijDGO z^pmRor_!Ea&yO2zq_my!cS-{wW7vV1+5f^`5;0S41J2`%QmjtBU6j3ZHJJAja~_F{ z?4w%^H$gEvCwuA+!4;V}*YjXg3U8QZ5y?@phT0>tlzthvf7)Himb}0hJ z;E#5{2;&{t-L;`L^jUQO>55*f8K(EyktS?d*91+vc21JiQOqHKH>@V2d8v3i)=aF)_6)s~-Yv=|cR5^?v z%q-j4b!19X4%9x(OE6z4<^=7Rbu#ms3S*1-IWfbT;aydlZA=RH;C77R);uOO&l#j_ z7hqTRdMy{bJQ8*h+zPl=LJ;;n61Fj)p_yOQK}y-U7=agbRb@%`R$5=B&x#%wkf+Al zmTKFEN!vCGU;fqZxB$sX+?wz!dt0mA#i|~uCx_u2aAydk>d6>YPbA)HSjfk$ml3uh zGx}v?TsV{v>tbZaC|t@(A#QWF8*4{O-fex%HQ^CE_9R{&>=s~`YN;OKmcfmW;a0&- zf$L+~8whTIs}U{|N7`p&O;+_~qh;)xKoyG`yQ;CGlpA^C=DQ2jmyb@5oJ<>cdieW? zluc=KN_Vt|7K4dj4KT{5D)YlqO5-O+N+&)N6?G0^%cuHxki;$6PbpoLuq0lYmC)g> z!LK~*>1DKSPF;mr6J}DzdK)J4S%X>9Yti<1z$L=9XMZ1N8JMXx z3EYbxhu{j~@_Cm>(wfZTat+*4Ayj!F-((D7kH0O_++ZYoZ4Y7Zp6GH(w`wkz`npvL z(vKa_NM)9CvH*V^?0Ni4Bs|uaD-+^x)(7UpyH3>SYm(Qiu)9_EM~YolkC?lg=Lgh< z%8v1i^}LQWU20vxiIXPW7w)7FlyL&?m&T8jcAs@`f1)dTZB*8Y&tXlwopw1_%@wkl zsHeT#COgS5CtM-ijl3Ie&?Ko$BI_xbt->rkZk4o`!>xoXkoH>1I`WOAJ1w%&xa(_S zE3vy{SM)ckb#PnZSkh3BgtHSa02dBNxJI}QaLKwg6`zA}>)=#e;U)e_RESjejjA@1 zhD|!x$s|DhNXKjyX6cgOJT_Dg+}00__Cd9|2^O-NrA@sJ!C0HXpN0?FV=C@+JDS*$ zgbkZOUCm?xLGF71~>3y_7|JDY`9f$512UF&z=2A+q#&9F#*m2XO3T~ z_y@-@Ts>m13eE|aW8#*>mBIBjajW5K;nbQRyp$u^C%y;nQL#5k)+y<^<8$>Il-G5# z{-TdX4`A25r%eusKpi%_08YL~8socn$Rb%DI`Gs}Gf zTqE3235Q40S_;?WQ)6A#;Huzq;LK^2{p?fVCW%8Hal0BW09T_KHT^iZaiFJ z3^xPL9m9Fx3SziraLyQR6qGHv3pj(jy2B$6iE9rAg|@wI-=~GM!ySe@QT*W% zE*~!G^R{`9aPm8vWpG(>VoSFtzo`lAi*8#za2sMc`K`^m7*2k7vnGby09O~oHNdTo z;r79;is25yt&HK4&!xSH;cRd#Vz_L$Wii}gxY8KTdLHdX3@5*JE{WlC;HJcI1#m?%ToK%a7_JO% zd<<6&R~W;sfOE%ib#MhS+*UYe47VFDKZZL1=ZN97^Jy<)xKy~D7%l@YJBAwwXOH3B zaJ^!xb9UCf#~SmAGWgx_=60+a?jT$^4iYc18yZ;2NO3Ct`;uE z#JS;Cz=h){b1RMk(w4)ibx@bAo5{>dpLcmMTa8(X_@U-Vx}i!xX4+S+A(pSyBU~L^ zGMsttu~qzs%QmG|ey_K+DVjR~*V5b?`q%&0-kZSJRh8?*yBSI)3}Fz;ARZaCGSqXD zG=o-6hjbneZGo!jNt&c>Xof?QmKH&S1*}>iB4V$X>&;a}yrM=$D1#Ugup&Yf)Lul1 ziqh|j8W6QtvH6~Nt>-=2d!4;^PPoPY|NDLJ-!EDFJnLQWde^(Aca59;pbm2hz*9Ka z_j!3AFH4x4Ec!%l7>fA~p_Ma#jX3+aR%kJX-YM%CTtD9kV(f;(RNc}t8ON*z?G2zc zahW+FjLH3TGZMo`otYWTA1y-I8xVHwb2x)1=@}1e)+EgyD9lI{pM{a+jCsCIPz%f6 z8w5<*9s#f0eis}Iya?=8A138}+F8h7U@5%&$~)>5=Ieax#!1lK3%Z2R;gS5E4{Q&x z&kBH<%c9HeAh#Yr8{v21@6kU?T8w_pIy%-(*5}l_xU8rhKaf(a2{x2%%e_M%BHPX2 zQTqpu56A1`sv{ET2kPP@pi6@e!y1;QHKv?Nx!DK5HSohUh9psuUhJcr0qk~Q5Bsp$z_Krlk4L2}q=7vL>^`AG^q5B3 zEtw#3wZpIfkAENgn)u-ntPj}i7svNGO1pUjuzkSZBt&bDD1Rpyh#qeP?%~Xk1(r+ zzuCWPY*M6Wz19Y!qnklD{eS*G_O#I9kutd(*fYS2q+YE(q8ziS*x`blN5YEIa2A!B z`8T0`2DE$r`uDM)=jx!@^9tFDx1$xw&9w=wobSEtC6raRJv(JhSobTPI>it}Wk#;q zpzZ%V>pj9&;ZM#3pM3!3GZ#Lm`@iLMKfg>?L(r-SCtF8$AYAX*-^adz-`+dyJP!JF z$=e;O49TZK&>iL-2<(e{0N4&-cS%@0GOx22Sk;sRV;2i>;SuGVppwO97n=A`4ZQvE zzXAR=K{?UJV%-HZK%x{#hnC-#Q@N+(<|3R6!f$y*SF?eoi*W`g$0J%-a-O-RMDvv6|E<7ErUuh^FEBYv{kTv``Me2Xk^K5@O8#MLP4vbY7gT~bL z;*}^*m;)_(z&ORPo+7f;!+RrW*WjG?-C{tPJ#yH>1YwKsk`O4f*cO9Xx1u39Snoei z(0OG=*BA#u8u2G*vxmzMjD2(}exU6OtVP|AJcDh4?Z^l|pT*kbU@Lq;jd!dPpD#X3 z4Bg_gOLO^b_U20aRgMLO%N}}ZY&PCKDGX)>!{+zd@RKvjwM;Mfj!Qi{eSFS(LD?b% zm2==FCmtC4SP_2Mwc7be%bca;hsRVCv1_dpwco|Nub-2)=_6JH=wCoB z^`^WM{Q+pdm!mbJE-71V%VrY(x8XeP5mM&yNSOJ+ZcX5|JCBd{2Seu0Jr%*zYLoM_>S!Xhe^VrZc`#8 z8*FGXsf^C$eu0-}9T@wI#6Mm)(QoZ6F?G|d%b7DT0i8?bEJu(&guk~2=OXbg51MT6 z24IV74~+d>0OZ4Vv!>Bu+upfl&3Ku2#I#lD&7|O4K6?=6MTGf+gux?y@l(M1ac25q z$qN*vtBZ>Ep^6~`m%>?ICi@Gdh+YL}X_?(8oB-*u82HVYePHa=T)inSg&NRAU_$}? zXW*|EG*xdsFm`B;rf9&hS#JJIi}ubkNVgWWEufwLwgY2DxpIXyWXX?&sSuD1$$=bV zD8V9ws?RfZ$Y#h~^KOY&n0IVNS}GOv2JjvN??-UXdra~g`uFegHEZC@kn8u_0*|6& z4?!TV2LVBV9JEB*;sCO!tmvNnaBBhiM_r4`P)`HMGP4hKEjr+;dHyyLd$r9uqoN-n znYQg&T2{2!7Aa^7?FNz{_c=T@AANRCS4*g?;`d8YMDYVH8Yk?TAQv2~u^y(u0T0Qa zz2I}~Lfiv@ciUH*`UKHo-(0CR(Uy~vI@nL3HDfeLM%xwY0WEd_vD!9B-9!Ajx;Go) zvN#j}oWy}g%5xgnGMtY;j`h)ue}JvU8TkPrGVO}7KgG8DpjI_0uRB3E3_4>w2HPK5 zH)W-qV;W{#4Q>DP0K!ejS^HF}5PE|#r4 zVD})KG}?NH?DBTPuMvK4V%vF*oHyfMWS)JhFWTdjN!Vhd<2?ww=G+5z&$&q>8s@9z z%wdb>pP?HCT|4M7B=EQKpu-+)G~_5lCLgC`a;+D1BSMEq^msO~`c((UmPp!696o#Z zjZ<>*NIKVowq^AJJ5OWMS#-JRSaj42zkTq#6YsV>6nz{zI=`&p2)RDZy8-??)`W+Z zdpiz0FFb5L!Vbg#Huyi5YnPx0)7BKvkTPVOa@*-^v%Uy8dLtx#ZCMsPj-8G6?E>^+ zIXiPaozjVH#}00AwIhT@Wf*NqJ7Ryr_Wi#9H@c9+5$`ww+MX8wZx{lS_d5{h6Bpt< zeXic;^xH^1J zau57bkGR(#vhiH0vD1n!u^mfpFORY36X0<#c$}$mTrF|Hth>5&XUR3#;tPLdGwfd2 z1BmCZcrUpqKOXdrSIc?G?Neb+%s}ca%XX2B636X?Ic1ot9QQ?a z$5zX6?>uTbCNl1A%gAL|gSdb9OJi-2r)0HBcTUFnz;1&7^h!v!V}p8=l-5YL$s&oL5DHc(C^E^iL%)q4+&y&&(i> z6@FJb^gDs|N6_C2znkG_=5`TY_}>d`5LgoLmcKoRDE#-r?*SjZ)XnFB4TsQ6a0zqR zT-e|5JCIv97Ayg*-G@nBRlsfqCT)UkFU+`7#?u_Ap&fG8k~bnu$@|B73!lq?)dQOh zpFxDV$>H+=u)UMyvmarq`Y>*hG~hAmpNI6~&Y#~}$Y2RzF99>-bkfP)Nn5v5@5B}X zy8#$l0rLnyxm#%f*pP(`Ke=aV*_8*z{>_KUolARx{mF;z0`|aF2gXXLSg;<>ZNP@E zhAgG{F3S-XNW);eou%+yB1UoR@fzHOs#< zT{_)m2wIEE^Wu#M#?C<|(tOwg)Pr09vGVpI z&f9R$)MV{Gkyq{p%i>O|f25t7TnIhgF?o5V{cT6s;oS$ueu#J4{dd6VYmjM$1fZSp z@5kL(AEv)u{~EU&rJf9e<|WWzDwsTsog7%Fl(^*nvt#d$wlDX?zZd>$UsfT^VT)iR z0DC;hTiW#om>G6D0w(x)Bfq4q9<=ijZU*k|S~L|u(574~=R*8vwvC;Iql?AopcRlN zAlO$+d$I#Os=jq#>@;cb3=cDRXKcc8TTLd116PeK73(CxZlPU6-V1)m-j93ViYz~# z3mb1M(T%gM#D4Fhw$RoI7H0y%PhX7w{ec5xpTs-!4Vi)&8_b|h!{HdQ(-wi|0nl7T z`TcYAW(36Hg>0<75cFF?U-A&fy2bc`^j;%&2gkZl&~}^)Nf!5DR$UT@(UmpE=4o~cjxR^u)DfsA=6sa6HLWCIo&Ap z`lZ%1#As{A@^3>G4?!1?V8HQxnFYc$#Edya7ZY7vHXG6GMVzNUiu>TCOk$wxKTkYf zo{A4efeaKMsKm^5PhX1h6Yh!owd4g z^K7oIVxK8@0?Hk7(^r8f^c-_GY{?PBQ;MHJ_J{PDDB&0r;O}1W8#ohps)=5MdHgIQ zx1(fp0>aui&w)O9_E=yKi*)8^JJ_+nW&k?^Y~>MKW(Kg+eVD{A>mCVUm{P%5?hunE z|J>Oo9aFb}*MZ(QHWYq0z`tDiy?0`M4}gBH@>`kD?-}@e;I|M? z$y@UI*_})>YPa$oi$-vj@;fr0UzIeH;D@HgEBPA=7V^-a-fd2LTR^`|`8@`DUwTES zmo2yaqz=j>I+A-Hmmvu?WICdJS#Aqb-0;;cA z{0~DIv!~klFVFc4&tdvwO3OTgJp-&3Vf=dD53CB<3(%Q7LU-7?_}0jmUNTE5iY0&@ zhHr?TAOIers{%G1n2z-YTLkQMVD)&HN3aWlrGf4D#gPRzI~^7icwSlYspbXtmV~(p>lORg;O@&;p*+dOdA@kgyS7B4K`gVUNm!T|rVPqG z+@)x1O&P317?f*&8BBohbC6}|-S~zr-Yrj*odr9Ki>L>I$Ht~idp+8lca*+Y-b>2x zF4*aCD@!jiSh>Z@YGfjIY)a{UMSl3uhIf13m#38ee#*q?wAZ6P)R!*xQ_i_>%5M5uS^6SKj{FPWuRf*}pZ@sb zG5^n@M&6S0cNq8NUrf0RHTmXgrO&;}`}H*NeQ{dp$g8|ZUuDx!QhuVL69J|jUs~av zgTjLMm-Bn}@ue-^IpDh(y33&N{rr^DKTq+#IR(&9r<7s}^7$!1?ps+g=QmarPcxdgBx_`Qj8W1ETkqJ}|}m=oHvoe^Tc3<{Yv*>@vq0{ zGyPA}`R(_c%zL)M$~&1b|6iaj*8^FT6}G@|^$V!CWkLP*QJ=$f({vl@s=f#Kdx-8J z-N}j@ML+V0m231-x(@~AOpn;#$pX_G70*BMf2;;Z4stp9$4dYIkJ$Vzwg26uUMA9B z;m04R|J_6z6ThF<|2|Q;S2#JIUl#xSL~{9Q{qGZnd!^HJu=xAaGSjUzyIG7 zk6$ML`~NNS{h!WnzfSz`FIPW)9{%@<`1xu5@Ba)pUPAs6dKr)Hm3i~0!TrwK|wHzB4=W(|F{IBD=G{mu(C38=!ny z$~Rn~e0>{U8T}1B7?f`}WJ}BR?mOsijTA+L*Z+&I- zmwsDNzI~K$fbw}ug8puLwSM%q9$fhn@)15fecukopSaz&f0M1h=|>K(e44+>?+4{u zOZf)azIX-ZZ{MSoZ+?)UtM5X8t9*&ag8Z_SZ;<(0USR%e{TONq@+(vxmu{Hl*>m{q zQ2D68@&fgz@(r5{klolO|GS>~P`*5v-T}%tMExZS)L-^nmQV5^&F2qMe~tGC<=aj9 zhACgNK>3DdA6)sg{3SJ?nFYg?Z-nyI7bstL-76!XC;NeX&pygGO8L?S$~TCeB=Q`r z{8jy>hl28XUw?V+Uw`*2B%e80Kd!)9%ID3q@ii8xKdry@%;#~AFYitD*T-_LT+)

`-+n4^{Ais^2ulxY>i>|V-ztG|d_GjAvrtc&@+n2Pan{FS& z4Vz1EaGsTJlDP6$H~9_AFMW@76(68{!_;50K>gKkp?sGF`6=JYy4pTx$ydYgR{gR3 zWeY5S^nl`$Z+7J4 z?{e8YiKmgS=F>#3%GdYhpnUa|FG>0O3zX0Mk|WdFMYQL^*3B=fA`bPzRUh@)ZcU)*9PNH zkj_*0wxIv5lrK&B1|9iaeW-jxjh4^B?tlB5g7OVfzDCM7RG@s?za1?3>ZxCk=~lh$ zrF>b+H(a26iJDhNe*@&J@{Lly$&N1*pL=EGOWzXA-x+t>_OFln8!1qK^*0|}`N|hs zeu+;94zngDao5FY0RkZl!$LzuWlw>8gL?sbG4v{x%ZV`Z3IQuut2= z5gSh17q7wo&N}T&Kk3SUVg2iW5R7k-@{O{6P8Zldmp^X#sC;T)B~%W&n$C$_)m!4f zg7WR7d_&aRh^xQX$^pXhC#OH+Dxd1l^DO;Hy>;{SHz&w%kiV02iOT^9-;>yF>&ImE zH+;I~qw=Z#(v)w2t}m+CSJmIhnL+v1zD)T>&Iy+xnuBXa3S{V%2|1#wp zHJ5^pk1th!wL$$2Q@&Bkmo8Ag`VR#4H(F_b_opatMKB&$-VtY9Kg@VXb_BysyW6%e z9_^0C0_7WerRp#FYI+isublE_3zRR*^dGGDPs^XjdaUx*QohNyFMXHUd^lM0H8Q>R z+k*Lz&`d{H^E8y@U;U zI0f4~YV8F*zkKU$w!WCV{y-yV$mPK?Q1cAZU(O86qyB$7U_YHcT4ZsYpfpdG2d#0) z)I7CvC>alqp_*svJnQP&YdtfavtmZ9JY?(bXLvm)j$0VaW6r+gZRl{DKE)faw*D$- z;|J|;933`~#)lhny97vIW$`ioX6xaN^7oc2tpES;_Yo?Xh5z~A(ZHggsRn}{NtVNU zx@o$NbhC8(==RecpgTx+i0&}m5xS#vz1P|DP);{NH%YgiZklc*-7MWcy8Uzq=nm2y zqB~4?gzhL^&s-9X^Zs-bbdz-J>89y6(#_KCquWn+fbJmOA-cnKN9c~y^~{Ayh@WnP zZjx?2-89`sx>>q?bo=QJ&>f^ZM0c3(2;EV-9zP0OPB%d}Nw=PEnrW>J4|TbVuoWZ(#g%6Lgbw>*=QHHqy<~?W5aIcYy97 z-66WebVulp()DIAe!2;|NxJoP({vl@X6g3P?Wa3HcaZK7-C?>TbVuoWZ)E&*6Lgbw z>*=QHHqy<~?W5aIcYy97-66WebVulp()G?{{B#p^lXUCprs+1)&C>0o+fR3Z?jYSE zy2Erw=#J9$&SLy@6Lgbw>*=QHHqy<~9s0G?eow(oI8&z`cGUD!lk*#Q3~8%g`s(A3 zPrUBb(`LN!%(D~|_mHVY(~65rON)z&ib_hRPAx7inkH^hNzoxCC5Ie>H{)M2fxm=D z#IfN^L;Q=T7ELY2gW!n2q@)21}@R+IxxXc}K->A>K!NwU-_vKH%{A8}UKnYKNVT=tRyT z2Y)y5VdA)C!#wg$5}_Xc5`xo&m#F_zYfW<2{Mhs|L8x50So)^qWXO z@Q|gSOZ?-+M~NRz{BGiUj#TY7bp-yO5%{AK_zMyEsi>G? za=wGOj<9)l`L__)`IwuDKSo^VbJQIB6Y(L=??}5K&q)}di99+FbQAG< z;^oZudPD7b7ZKNasndvmjJVEsY5CbrT<5EG{P_%Voxj#`nP&!Z-4C*uIIcUja!Lb>=TpRciF@2fqWb(7;`PM0 zGT*;JeCREfkCwyV5Z8TA5ZgRQgFxieeNdW@=Mwj3TRsa&e+hA2cX=c6n%Pq<%5x8K-FJBf@!u%@TWz{fY|L{cnog5n#I;|mC9eC< zwf#O3^_<9;i2szho=bU@xLgt{^5}UVE&r#XLlL~3>$WQY zGUENjwSC?ef!`8=KOTWUPh9tNEC17=D3Qm-&m%tQ$n*XP{PV5B<0^9fq#p*uJ0=S?<4S&(BOzXF8!hid~*c;Y2qID#c8}l5%|v|@Rx{} zJA6(&hULe>Yl$Zu{QL;Kmw3{lmrq}a9$b8oc)dd}pWYC97k`d;+Mz!b4V2Kk_zdEW z4*h}%yoGqyq5nVxK0v(Bp}#Kze~Ng&L;u$Zd^#?WlynU^^wkmgnh3m`_@Kk5KLY;} z@gax)kqG?1i4QyU(~h(9yZGygk2v)6Bk*;^M;-dU2>esTJ+7l^eYrORf0B5)L%%-) zKMoV*k}nB|zA6G=MLg-yXCv@yiPt;yw?*I&6Hhzz`y%kE7fp1{SN)!2>j2)2ORoivG8K*i-T7XA9V0^1l~z}$f4gEf&XU&z9$0z z4e?=ze+d?bM1B`PmH3E5Umt81Z`I=3Zj>?1{ktPP~!yY8TaE!A<1JI{Ysu-bY;R+uMi_ z6Ic88$q0NJ21>$T_u;`jFi$!H-$h*a%W1sdC!Xd0x$WdrgozZxpXEPcXUhm+)CZ2ZiVoY>Mxovdt*AdS;_}hv1IrzJY_dED|i4QpV zzYrgE@H>eQIrxu=4?FlDiH|t=QKvIs9Q;khbzh+7%W~r7-0z|ICgQpePVo;#;Gc`Y zA0(c1`2UKy?!!|4Q_isRrycs&64!l4O8-{ky5C3fcMP8o;$gZ_y*#7 z4(DscKSEs3(J1{L#Pz)An@ImGaXs(5op{+wN_e6TgGF-q)~)_%Dd-y%4L3AAPo!NAI0Er#P!}0EziFouJ^*2Z#yII!_Kkt=>0R_pgeCSuJ`aWay?<20~i-$;m(o8E)y2t84({&zkJ@@(p(tnJ&p0E8j@t+Xa^P^f$4#A5l zpIa<{mH%zTlRRhoIQhJnxb6q!c6#p?;=2Fv?ou1zVWt0o<^R#C7XLl*G!rzN{Etss zd2~N%ZL#&3OI-KW>UQED;<`VU)7IXni0gjg?~?!h#C5;#_lW#y=WOw0Bzg_S4Ca{^CV za_xyJV~zm7!* zCwlX4vyA^j`nN0oC5zug{>{Yo9O%!9UrSuiuYQ5}UBvY~@57Y;XUhL;7SQcj)2gU9 zo+Cb#@>CMnbIV2Ka{+NZ53V1dd9UL4SVAoiwdyKf=YcN3h|EPS1EWP%p zr@+84_3>L4e~$EXiR(QT$5H+c;(EV}^4~>V?|;$u?OVk4zMp5w|2g7%&(kpRKZ z`qjjD?G5tD65s!mAbt(;wGRHD#COX^4m<{FUuz8}S#2x71BB2!?6qNzAqMdft0F@r4n1CviO&u6#aDT+cl# zpL-+lUlG^y(aPu0c~&01Kj#$6Ga~|DKsNfoak?(yC}#a9y7`#6<wRg8|An~TQ>OT_3#fnI52kpExZcZE%XGb)xZa1Q^p_IX zd#DuuPvUz2lk)ivalLm*=|_p{eMgEPzR=34_Y5hYGl}c{K8i0SuJ`IFpBCbJpN--- z5Z8NTl+Ra)>-{ZC|0r?2H%0N^64(1m6rZ-p%CGl?DE~8v>-`*x&n2$+Vkn;salMa1 z>8~QL_dqEA72`i6 z6W8O|vb3z4qfL#IJq3#jTb+?+?U3N?hrS zR)pu{;lPFe&|J%hVtPjsA9e6kiM!v|lJ7*z`hw@IFU&Xit35q$s{A*RkAAO9@kiJ=<7q!IoK9KEX(-A>`V+4LL@xJd`yXY+Pd7il5N22o&r>Dc^xe&PL z9fj|(2h-I>dXEK1=Qln>T<=>^`S(WfpK@+^ymiF&y!IiKe*reLUq|Oj_7ivYFW)l`%a@J_{Kg3Un-Tav#d%Lpit^7`9Ukww5%`B9@UH@&g45gX zIj(O;(C;Jey}_1~(y3OUBiDq>^Je0D&rmPr?~9<{ZSW&KJ$FuW?>^Eem@hi7ReYX} zx1Q&8G~bsIck^p=1pX=FgDn4VvdQy)p!|8R;ZO=RN?h;z(fPMC&kxtbYT|?ETgErA z-MG%+;F<`wlOHF&-j}51^I_mqyyHFh-1OrHKQY%%s@|R^KEiW))9CRx^4I&6RQ_2P zSo!rnsG~@~fw;n>D^+>Njdei88}*7Z*Yp`{~FB~L(d;R=T`DbOtX6a6a5}1pJaKE&x@qb zzGTbo3#32pT~?mNt1O>V7T*ToB2SX?yq5GENuPee5+;dnCw=)bHeP7TJOfJqjHSo4 zm3h8p=#TS8zG&%H55FXze#Uz;>5DF;oGY#TZzP@qeiZER>{o((-a-0-r)<0*Cw({Z z!S7o9S>m@4Pd{t*Am5yq=XT=Rzu0*9+i1L>5%2${rAPN_o>AbUx8Y-hdN};uR{q8{ zHeRy#PEh=4%l{YF@#YXOKg|O3$-jkoc9o6yO446Ryzf_*|Lvsz58`fn^&R2^axfFm zbkaXTeE3w$_^rhML_G17jrUmM)81p#HT(|Cr`f@hu&m^AZ{rE=`U#s-PR?ahtU#;|0ZM<6Fzes%ewKiW= z&yN9@boCz@%$HH(NhiOKYO-?nr7fQuJ^L&LF7-O`Rm(@)hjxRboqTmrZyzGw$ntg$ z`P@zZX(zv)Cwj7 zd~t4%cpuwyt?zdkoJ2lnFX_{-v;04(el1r1LALW<#NS4IfD7^u7F&<^5U)qCEsv^U zi{dPY^DVjeY2w)jECXH7eU$jfkF8=JB>mr&5BI-me^lKna^^pcz(t=~_B*GLAiPv-EKqL8dDL&0IID>pXsyOSr>fs*XlHR_q@)_dBv5!#BH?~HAn7P9n)mz(voav>UaYoN$qqr_ssxHvaBo>>{76)6RdL^l7#mYIi+D`hKTBoq~!f_I4xfsOuQ->BNUvA74kjns}D& z)tiWK0519&Vg7nkt;Y`H*`sWEP`!PYeClaG98NxWk$%*n|FQCM?DoHq&(NS%%+<`_ z*R?MDfh}9IUgc@g7;w5*kpS@B0j_h z{tEJWN$FXSjwF6chs~D}-h0Pp+M7>&kammGcPTyX)rUBNayf9RFC&iq^C{B1_V7K1 z9`?CYuJ#g7(0-UfIZHZOe$rO|zoq_P4P44Y+9~H1O7GZL9mKP=2Tmsc8w{W0yd7N6 z)&AuRlh0duDp>E{4cxR> zj{S2f=?7STj$qs$C+^-i@-4&1Cm>&6AiZnnoOm(iB>x)H&oX#EpYupR@Sv4{4oNO0 zp5Xl_dOyb3ftzyWr0a*Iuc!TwVWxThKs-%5JVpEs-B!-w<+i|HWTW?%6Ysy>0<2En zhk#2xn)an&zxD;CCx0FH{ZjcmF+T(*6SVle@6bwc@2uY5XUS)XZO4T z|2~I*59u3O-wz?5n}`oSY13Oy{1M{WAK7wp3)_JwiTB@c@x9E)zY(wJxGAA>UP8TH zXbI0F{TamFb(8tT2RQycmVA1EOF2n8^|YV#qsLnXJWu+skv_rpQ~Q^vN#Fk)OaE8W zzZQn2@NZ=Muk=fSOZ}?nI7{u)jieuS`n9V_-~a0%|J}-m1y=R{Tf^r#uhHpGkJ@JA zO*-v!4e?R7hcI`|^KRfGzsK?ZX~eH2KH%8N|3N;DELSk^&GWFKKhE3l)a(Byeff~h zcbyk{^`+Dg+ocNfnQw6Z@Hyv`zMSJ1G#BRi7;$%ecV`5jMsZ z(^f#WH;%fT`OXDN?RVZxJUML3$rr6y-bUhswExQ(?-j&HInG)~ydSu!?~We6Ncui! z{P;WN|79zu=5N{iM9%!@^}wZ_EO+eog@zvIL8e&2&t}lg#FNDPh+l8`Air4eUPJtA zr1v-vsdmB>#LH>VA4>Xt#QQjr@I~UszTfJ3Xsy-fMz+Hh#NBpjCGi33S@WxvxI0hv zA>#eCKUHrJ5>MY{_1Vhu`2z7l$38i`&&rveYvp{L@~4ZJjapGHbc)JKIg;agV&aoR1Tq<{T( zo32sXrR#{FN4$QeEe~(9j`u#`QXi8}ef$jR)0F1~@_B%`JFfXP@qve}fTxlExGSwZ ziF1n#Qbqaa5YHZJ@!zmxZX!N-p2hz}K35r>KYY$E;Mzy{VDAxwALpf=bp6iY7|%N8 z_RyK}<;sq|mB3DtTxr1*53uCJ2*SH!biU(^2O z55&{d!&=fG1H)eA$v`alIr;Se%JR|j_Hp8Ui;E5QY0R%Z z#1rgi-$-7g#78LqbEKd0L0hgyzfxi-wZA^y;P}P*bt?HRF*twtoOc5^`e%D}AN?*R zp5#KoC#<;MmE=F_^b4OQKIr7{{p2%rq-F31c94H0-tX8~XY8=@G}7=tndx0kJi&J9 zM#{Mbxa5mRdsX|jTS#C3xMgq_>Ay?dWB>kU;x7^(V1K=sxc4EJhu>R1^~BErF7YOv zd7HV!M~JID-Ne%zuW38BllH7z5dev?mAU^U<%jfl!=RxA5&)Ra{cck@rp18Z-bmT{@Jc+w4pL;1! zHF5V`*ec-Cehxb2yodB@wi}Bn&yC9GJ}ZBztsdSU;v+w_@t#Hc9|AXe;Q0Er#Q$V) zj9&&VgFfOX@3is{F0p)Q?tAlzXGd+kr;~mI@u9mdeynx8Pbhwp)!VJ4|32|@uH(!l z{vvRZv)}1AkGjUnGjxdM(@pvm@%~3_gbl>c2QGZlPC4l@_;KElqvspQXMpqM_-USd zi4U_sT21^9hR;dfG{>%(eJ%Co*s<>Cpi!HN#Yj~Z(L>dul?_h#Jv)Wb33y4 z72uMtL8l*ijP$M@^#bt`XPi>j&wS^^+liFtB!l}Dz}Gh^J@x;6;^z|2o^0jpqW&)> zKKgU3AJy}RBKZFs>D_VSgAw#k1DATepXVIYl;_XnQ~!*WN7rYQAGP#-PWilm_@FZ{ zv<o6L>FXDPC-jHU{Vw^8 z(r#Huzuyy2)BgD~@snUsN%^dI?D;vsMgAeqTMm)Fhj`jqzqr=mB=R|*CH+W=EkE0t zzk7%$?y&d}@qNVIeL;s};4JbCKV#|NZ`JA@Lwtzsg<&NF^2j%k%D z8-WY|p`$H-)!Ro6ew;Vzw8LK`pY-ikPOW$UMLt=_{`|9{rw~48Dmq-rcXyql61d1S z?5roI4UYW}w^=y{$m=5V>8HJV0r8I#Z+ywh`7`SOOTwqWt?st-;PjX&D_0|kr>gj;fk9-*TseT50zcqsY{p4T% zgq2_SgAEfOWV=yKKKqGh`Te(6;$y%iy^YR##9234y!-^Ke=S#Y4St-r&)N5KA?f>B zu1+BTZN!J2a&jYZDG!6JkM*R#lX#NzPKrN7+}%(0bMhZ_?CQS}A9lvMC;yw(|8ABm ztzXp!hkfp}1FH>A;e5_!@)>c)7rTH68ZNphpSYr>b$!$3 z_TEezLNvJksmj%D?O>9byLtWk*6viYxxK4-Q>MGAx3!^t0^+)LO+7uC_1&!(H?&Pa zTGPC-sk5`SeL}*z*3K5NZ0bPt>xAi2f6`rPD|?zcvhA%snWimGZS4(PCXgak(cRkA zl3}2Rbk9U7OwEF(Nl%^Rr?jd8nrrUup$ZF5>imic1)iS@A*o$~1V_a=6WQ0)-qhaK z)Dxz?-q!BcF3sx#wCAS}CM=jQspVarli}+xbMuoXk$Ht2>1fWhc5Z2H@5;9N3b_>b zIw|g%)yr0{X=rPxSiQ_Mzq?zJT{E*y-MurDNWIIkqN8b3>zelVHBIZ<{R|swHnsN( z<`$o7TPbEWx3@NRH*9fq8z`851t`7CF6Aehpr-jrW9wOJZhKocvwB-+@5W3jwWX^a zZ9-;qXIpPW%eW>2?MG0I39H1iRBdNhTTg2y+uGgM)siuq%4`{4IP_PmF-kF>yqv^X)rI!YaMblvnHmgE`U#M&4heXRTJ|`)mBZ+r>=5B znu_Ga@>CQcPeoOAbX=(_tLV&}Ev+lsI-3?RTbF8Rh^DVdlD?*DYpObhzlyDu6^oa( z)mFy^%Vkw6l`<^}i$Gqlk|V5$mLW$|l}a_GDl(>9ZEeYK&c_Msx;J;C=Jui^%j=hX zRK5-}vOreVcD8OskKcW994jADXnn{)~-D$!=N zZ_9MRK`RrT^y*}Y(nIxG9j?y+E!uq3R%OobZmvd6sBCBm&4k!M$%sHk7!=NqCN`eIlSmcU%ts#wldv0+oOVN-c(&&dNIO`ix<8z@z%!13*VTqv*Jo!3ck@{la;aBsEU;e zzA>EZ;&Lzv-?*@GnoQQkX)+m``{ElDZ*5&n4kqCn7dAG%wXx}~sf~*lzA^FE#Fh3W zeB;8#wG7FcxRxPV9jhz&#>874S7MX!jSCxBCX>~%WwJ81T){Uc-q_YG8QYpAD`RDb zZ%n+YS#f0pzHwn=^EVZnzo}T6ld<`WurX34W8;l&`IE6NKc*$)bOqlysbb4UGPZ0a zW9uM%j&VXPxG5Gl49qK2^!EoCx#D~MP|oRcDskUM-3 z89}TC5(B{yAvIqy#EkQ;iuFy!`X=LiVc^8Xi*7mESB6}1zH#x&peiOT21e1oGPsHM zm4Q)=F9tW!zA}7?^No{Ph9EIvG02GXjg1!rg_y7y3dH%wl?^fWW5U8K-xyyQzcIeCHAM{AxUjLhf|(f?Hm?4PAsH7IJz7EQ z!&n1L_B3s2)vW}wZdoM@f34jX%H;3I(q=C?E0wgHmBzOrOc*%4cPiAh%eP0xhz*&u z5ALg}wo~Zysv5R5q?)5vN3oKdl)Y5F-L10u#%!yoTPIsrS}%@KgHd7O!CYHTY{Hil zEu<|hi&J%lEE|GdVFL-xeWR_HS#l)oq)U!Q41E`taLJheMJ$DA4a=D?C+|$Km#I4} zffizSSBBM!FuOCEoOlZj4DJb6vOrUCl$8^&?_pl3V0P6p8*_4{rn+EuSV^5+RVZK+ z5^RV<kHy+{RaUn^i= zC}ytKDYvIza0&}p?WE8$b2eW?@ z=SOA1JqM;HCT3S%Kw@E)7Mj?a0xBl>kh3*~mndu{ELh0eiPcIil0y`VFFHimMerwv_eCO<3{Nr?K0sfUW?ILO(?^Lq`Uc3@0!9%q|$xUjQVlVYNRt~ozYuh{~j zA(Rsw;hxFbSuvi~bupfmVQk%eMJ)+o;Zk89F@#E6&{Uih$uKE2WU?}bZ88<-SrzA5 z5$74F&}2Z}3?Gb#5Jo0OrYAz^HP+g&;v8;> zgcpGvqqw497pIlFxNNNpl`ns0BfQ*GI80QAs31~h#%Kx&5U!T^m}iBRJQb@pOst$| zj9M^B86qD;V(Ku?GfbfxGOli7wlRinRZOM81YulABWY!Z(I9d@biSivzi-rLoGE;|Nb%rXoCTX^il+ z%`Lqn={7w%ablg<ymO@D-Ej9ReTW!jtYkhQj>3VMdCMSHv;t;)x)Gw2zqvs^zMq|Y(4ra{m% zL}l?J)mR%M$VH=|^%3D2r!rbR!H|(Dl~H_*{y8EEGE*68s6}|jR9qY!kBO6JWjNco zDvvEgA#B~YTn9fv{`p2Xx4Ra=F@v2MzFPGnF!CgblO4hid4l#P3zY;bsWTC;wZN`Fl7zrjB?|7J~$Rf3ws?2s}{Rs|D?_`2c zS_F}xAw9WxHH?ky;!bYw>p?+xA?!@bK^OpJI$Arry0>MTu$d}{o2!R1GAVDN=I-8} zUfj<%v)OCG1zsE4dT<;i)7v4p%5=8kqOVL#S7t+d*Se=o zWapfhip;_l=VcleQNxSouX6S4S_A02Ctf4Si=Mr60jicAO7TjJv}Pv=r&(0k@>oc^ zCx4I!Mn-I)`~y#u%~QlML9!z)S$o1QUe~$uU+A7^Ty0VqM{E|0Ck)Aem$e@hOX(9t z6t$K)K{z$ALJDlArKc-{8!ub(?-W#xVo5h%EZd@1wsvf8$3=9!6EpvUPn80jk|tay z%r9;)XJst3a!a$|rboJ$LYkAwFCPZh!BRD5Zp>aV${WSne0KuIG_9&UY*lfE4{mbT zTtn%OCp4>ows~K-t}r3kE=Aohmf!rdF`Y2KHFY%;rOvDw=9RQ4>BSVt%hsqu5p4eE zzhlLm<%{Q`M_;pi9{cn87tHK#T|YCE+0bn6i0tX=#0a5xTV{*g;WLu1sTeHg(i5T()@bytQjHl{2gI3S(s& zX8tOir@*MZb3>gu3Dw@ZzBinEM@RFl$Wu|$K-HN|N^KbLU^u#Qg4pDy=N^oNS~HO` zI@dN!iUJAdZVAp!INHzxZCCdK;cDfI#mt=SX0`Ic*jWNd<>}I4kH%;GKIJ^gL>m>A zt84=2Y;I}mviD%xTWaOHQa8T3-ljH;FSe$z=QmQxrVz~2=?~w9<0(f~_IP|?cS@U= z8}v#xxw;gILszg&FpPqI%tZWP(Nks1M6J2!xusRcwRw?1=c%e(AvS~L(AyeTUF{5Dwtb#83x$*gPY#ZZ46 z_5gM8itEjt(F&3^1*0H4fv|2{Z)?vbOrTS0qKy_z*Y9~H6&1FoqN7rF>4oewAM(C4jMC2#Z)GFk$p0`lWl6p{7xP} zX&4t{a)!%?ZfzB%R|b?Gyq48DLuw62ow4%OTVoeDWjC9<({=Ng>?4y5H?2yLum4Wn zRE4>k*WO#)lV1a*ahJ)uO|2Q+&fb|>w;4oj`R0D0r9&YTim*af;ZJWKK?gSA`U!%$ zXBW{^rW9qnH^h{p{FYI49T&H@J(YF^DcCSYnnbxh76Cz>uNOpCmv9Y5E5L1^>!doh z;I^8L4S6lGY2;J0HfLL8P}$zpgIT?fY<>%v$20C*4^ID3-Q3l=zHNg!ZRMxnU+y657}m<4TECDq@0t?X$IDS2pK!uc=& z2iP}Zob$4%>}5K zI@+*s6?S&yOeoVklLshO2|MNv{;B^|e2bu%AelHS`XQ0&EYyJASq@L7)S zT^mwKC)30ECGB>f{1WMJ=+&yVRX9>M+%AxxxoAb|?A;%dwISDkM95_~Vg%%Jnr9s@ ziM?%_QLI(2&udF_{ayvXaMg~Pgr;sgUEJE8U!-ku#B2jd+Oec6NeY+IY=1);@r~8Y zxFtFO_pSwv$qAaZar4HlWx+C_cD}?O^hs6QjH~q7yC~2ob50QnXj_}pg3gGAD+u?w z>$p@mXh6C^EaNShF$FiSYnPE=UX`XWF}DJmY|CpK-00l%ZJf+A+bhgq)yyKSYTXc` z#ftSZ8nh$q-n@dIGtT0TQ0~fdNq2Fm?tnO z^qX>liHX1mz?{M*dJm~0?kswx-8>f%FVIeh^|Qh|X0Q}w7Mzj@8z@w-HUo`R%6{su z=aSaetSxE*GuyS}QkDE}obO|E5qV%Yhy_cy<_`|tyX$lfRqdFp@?e9Z!-%Vkf!qkQ zE43evxx|L4A2b&T9-)l`C>u!Ir;*>#*Pa3d`s~J@8v7c|%cYI%wgq%kxY*D-_Lg9a(O@Adp4c zSmZuV=j3iGYKA>uWByMc=4&iu62L#5f|J?7FF!`luEVxk3b|;u}0ztm6$}U0$~x7y+a@+P01h zu{7jko>96o^$ey@b9P%Uwp88%R?hOVBaQfb_?+%Atk&jdcCfFC|01N_IcG${#}u(a zG4DfUZqZ29h=Q>3whPl$*d5V~`6~O#Q&(6=Pe#x{U#O~>=(}xpL>y!1!%Vi#iL^Ct z_U76_Ew(O!VP9>(afzu-W2wo^#k{=bKnff+xR;7wQ3?CMy$!2qxfyIsnl?9WGh+d# zpyqmSvkg1YG)PU6rE}h(u-Q(QJK-bOwet#vF<|rCchew;G!7De!nk}p^8=#ijMex> zPJ2oq5Hdze-vA^KBslyJ-LxZVamFk)6VDfE;f(*zVvY*4Tp9VE;08|Ly4tI?y&k>= zh1NwXeaOtUB)?`o_GN|5Q`n7Lvzjmq>yImKrm~zV5r0pYR}^yfAr)L8Ym+^(LFaNYb+0FL*+5rPGKc{jlgy<6|@daBdDp*f~MpMp;Hm%+I zE_{YyRV%)^-6L(DQ+C-(;gZ2T2`T^Tu2S}rvu3$1 zz|l1Rp$hX`1%7MLP1ksRs^N?`my}AwRKYl3qM%q zj8r>r>B(1nVEwZ)8L??2a$=THdlFu!m=lWw)m~*okI$ahJ&>39RsxcGx*>>sAqc>#bDzwod zv$>^peG@KI;q0DURNNK~bs>NF5YipTl=NWY9Fr?Hb?z`KcI!_d8Z!u-WhSQW#tD@4 zuoWDr&+a0?fee#1XkS{*CeMHwXjY3-c4L6l4;fQRSL9S;d@wxsIYoaL>X2z`Wv_n; z$ZhJnOt{G9*}52FOrTlrVu+h_#xkjG<*w6-?h9;qh;5YY)dd0V=GsAy zt(|;f-`U``Zf%R58V>Y{q2qL?l!x`UZcU3H1qGt4i`$dn?-Wuf+#Ov~mN;b1v!6^w zso~-odxec{y=Jk~ZC67FwgCkPqzvz}eEa(*BnaJa=a^t>B;kT7eyYgyU_BwlUfS}w zZqn4C3QlYF-~-()rtNEKO1oy7Gp$#V%%CNoU}n$Nrc6Tw_9Qx`k*$PY`w>3rZBK>= z^S~*4IXlWxTp+IsZNA65<&eAc@~6#$f(7k6V`ZT-%eG0`+#1%Nac3^fayf~aziA_v z`LcdE$#11gZqMr7Vg@oD6V5;jb!aV?3N*giH#MuJyXg|Fh&6B8&}~Xk{^EPs6p3LC zWoYwlT5joJoKay_Z*vkS{$e^8JtT(vcgLxEda)O|JFcZQT|&@GkqL&7J}z$=D7@U_ zlY{w2p`)xyqb%F%^knS5i9nIj(Ouykzm|wPHFHddZZ#3I)+xI{QtR8=+p&*t{dz8k zIthy0?4~y&1Vm=Fvf~Z(4VWZ!OSaRi1zL0I-Dq;V!E)A|yAB%OH1M{Jpl4)Sa`#vS z=_BVf{Jo40HUlP8pn1WU+)){Hx3irniqRT&D;cdh8KGk&d(}iRyi>BOooa}}m0y&V zpS^z|z;C>rD}1Psvo7kezSh%<_9$d^UCU?K9Y}75j|&lIW8|W%HTzI)8@VyRgO_H` z9AdW9drpHgseR6&4WQHAU^O3;uYq}2l}?e0ADBVXh_Wfduvjl zkQ#d?xC<-b&&r!;Scb=d@2!ej-5y5@Nb~?#L`YS;D}njDZ=K{u7`}*f zRnjqpa|JkzhU#4({Nu}>HK#j$w33Cm}l8?f)V&C7OOvVOd$t#K=LuG)rLziur4tS)oeFUug@E! zYh|5yrqz!0 zhF+%-W?;D6n4Fl$Z7`?6Mh@nh1@`rBXS(6+W*lhS($?Jz8z;~kOVc0P@WY<4J4xg? zv#Y*bMdmDTmmGSO!%7Hc&rG{I$-6;>_e+?P87#t*g$52zCa_wE1mHMZFS!kJ%gq#- zyB`CUk{o>_E}EdY$_+XRoz|ue=_D^V(zo|k1|={H&Hfy54O?GX4(E#xlbK{M&fAA# zt6OdliZd#4E1WP1Xuv7ya>M}3bGdE7_AHmb2{Pp_z(PBW{BcfR!#r-m)?!eWG3ULo z*RQ2*eVf_W`(wRnaA zR}gIUX0~kW1T*XQc3YnWRfnRrPdy@p>MYv0UwqgqDZC3m*{!ERD!= zpbX)=dL?2YCYp6UJs$2>#DT8PUhg11CxOo)_$wm6Tlu1Hg19=<^o8zBxc=~a7+&4e z9r_aI_Z|;$186um{-y9P!=Hw)Ew}VJR|LdUioYrN)9}*;#IqWI z#rV_k*|!PmdFoD`XMgK|;o*PYg&$)+`L|qQebs$ZnB!6TH6C?0!ta1DeEKRw?x|bO z2t|J3q4HZQ&%1=U@@u@>`sCaX+TR+#hS&6qxZ*e0l6gai+X&U&Pkjo_3HWove*~C> zn2JC1Bq%UlDPR6ae*W-%XW97GRXSy%zt#Pu6TY9} z)lE+j|7V==UMXu6d^F3OA6nYFV{`iNlx8c>@qJl91 zwX4CsU*PQs{IQyOx87#M-+GgE-T3sky1zr%BYoiq8Gf+9@c)CbB9q3yo8fmee18(2 zzDMI#Xet6r-O=!ierDxf^nL4&GQm>+<K=)} z{_x(24d)G8cSs{93k|34bSM1KuNm$~)~y%jz9->(m)BF^FT6DU-m{GVC)S9NzMO7Z?%e>^(h~S YSB+QEqwo?I{_>yN@Mk*#oS)wR3p?^^bN~PV literal 0 HcmV?d00001 diff --git a/Dist/Package.sh b/Dist/Package.sh old mode 100755 new mode 100644 index 7e253e6b7f..830657b614 --- a/Dist/Package.sh +++ b/Dist/Package.sh @@ -13,7 +13,7 @@ if [ "$1" = "windows-x64" ]; then rm -f "EmuHawkMono.sh" cd "dll" rm -f "libe_sqlite3.so" "libSDL2.so" "OpenTK.dll.config" \ - "libbizlynx.dll.so" "libbizswan.dll.so" "libblip_buf.so" "libbizhash.so" "libdarm.so" "libemu83.so" "libencore.so" "libfwunpack.so" "libgambatte.so" "libLibretroBridge.so" "libquicknes.dll.so.0.7.0" "librcheevos.so" "libsameboy.so" "libmgba.dll.so" "libMSXHawk.so" "libwaterboxhost.so" + "libbizlynx.dll.so" "libbizswan.dll.so" "libblip_buf.so" "libbizhash.so" "libdarm.so" "libemu83.so" "libencore.so" "libfwunpack.so" "libgambatte.so" "libLibretroBridge.so" "libquicknes.dll.so" "librcheevos.so" "libsameboy.so" "libmgba.dll.so" "libMSXHawk.so" "libwaterboxhost.so" else find . -type f -name "*.sh" -exec chmod +x {} \; # installed with -m644 but needs to be 755 cd "dll" diff --git a/README.md b/README.md index 6a099f28f7..84c496975d 100644 --- a/README.md +++ b/README.md @@ -316,7 +316,7 @@ MSX | **MSXHawk** N64 | Ares64, **Mupen64Plus** NDS | **melonDS** Neo Geo Pocket | **NeoPop** -NES | **NesHawk**, QuickNes +NES | **NesHawk**, quickerNES Odyssey² | **O2Hawk** PC-FX | **T.S.T.** Playstation (PSX) | **Nymashock**, **Octoshock** diff --git a/contributing.md b/contributing.md index 05eef20c3e..98fc456aa3 100644 --- a/contributing.md +++ b/contributing.md @@ -23,7 +23,7 @@ Jump to: - [NLua](#nlua) - [Nyma cores](#nyma-cores) - [Octoshock](#octoshock) - - [QuickNES](#quicknes) + - [quickerNES](#quickernes) - [Roslyn Analyzers](#roslyn-analyzers) - [Virtu](#virtu) - [Waterbox (host)](#waterbox-host) @@ -264,8 +264,8 @@ Uses C++. -## QuickNES -> The unmanaged side of the QuickNES core. +## quickerNES +> The unmanaged side of the quickerNES core. Uses C++. diff --git a/quicknes/bizinterface.cpp b/quicknes/bizinterface.cpp index 0662b85ff2..45a8e7840a 100644 --- a/quicknes/bizinterface.cpp +++ b/quicknes/bizinterface.cpp @@ -1,35 +1,9 @@ #include #include -#include "nes_emu/Nes_Emu.h" - -// simulate the write so we'll know how long the buffer needs to be -class Sim_Writer : public Data_Writer -{ - long size_; -public: - Sim_Writer():size_(0) { } - error_t write(const void *, long size) - { - size_ += size; - return 0; - } - long size() const { return size_; } -}; - -// 0 filled new just for kicks -void *operator new(std::size_t n) -{ - if (!n) - n = 1; - void *p = std::malloc(n); - std::memset(p, 0, n); - return p; -} - -void operator delete(void *p) -{ - std::free(p); -} +#include +#include +#include +#include #ifdef _MSC_VER #define EXPORT extern "C" __declspec(dllexport) @@ -39,42 +13,50 @@ void operator delete(void *p) #define EXPORT extern "C" __attribute__((force_align_arg_pointer)) #endif -EXPORT void qn_setup_mappers() +// Relevant defines for video output +#define VIDEO_BUFFER_SIZE 65536 +#define DEFAULT_WIDTH 256 +#define DEFAULT_HEIGHT 240 + + +EXPORT quickerNES::Emu *qn_new() { - register_optional_mappers(); + // Zero intialized emulator to make super sure no side effects from previous data remains + auto ptr = calloc(1, sizeof(quickerNES::Emu)); + auto e = new (ptr) quickerNES::Emu(); + + // Creating video buffer + auto videoBuffer = (uint8_t *) malloc(VIDEO_BUFFER_SIZE); + e->set_pixels(videoBuffer, DEFAULT_WIDTH + 8); + + return e; } -EXPORT Nes_Emu *qn_new() -{ - return new Nes_Emu(); +EXPORT void qn_delete(quickerNES::Emu *e) +{ + free(e->get_pixels_base_ptr()); + free(e); } -EXPORT void qn_delete(Nes_Emu *e) +EXPORT const char *qn_loadines(quickerNES::Emu *e, const void *data, int length) { - delete e; + e->load_ines((const uint8_t*)data); + return 0; } -EXPORT const char *qn_loadines(Nes_Emu *e, const void *data, int length) -{ - Mem_File_Reader r(data, length); - Auto_File_Reader a(r); - return e->load_ines(a); -} - -EXPORT const char *qn_set_sample_rate(Nes_Emu *e, int rate) +EXPORT const char *qn_set_sample_rate(quickerNES::Emu *e, int rate) { const char *ret = e->set_sample_rate(rate); - if (!ret) - e->set_equalizer(Nes_Emu::nes_eq); + if (!ret) e->set_equalizer(quickerNES::Emu::nes_eq); return ret; } -EXPORT const char *qn_emulate_frame(Nes_Emu *e, int pad1, int pad2) +EXPORT const char *qn_emulate_frame(quickerNES::Emu *e, int pad1, int pad2) { - return e->emulate_frame(pad1, pad2); + return e->emulate_frame((uint32_t)pad1, (uint32_t)pad2); } -EXPORT void qn_blit(Nes_Emu *e, int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom) +EXPORT void qn_blit(quickerNES::Emu *e, int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom) { // what is the point of the 256 color bitmap and the dynamic color allocation to it? // why not just render directly to a 512 color bitmap with static palette positions? @@ -99,17 +81,17 @@ EXPORT void qn_blit(Nes_Emu *e, int32_t *dest, const int32_t *colors, int crople } } -EXPORT const Nes_Emu::rgb_t *qn_get_default_colors() +EXPORT const quickerNES::Emu::rgb_t *qn_get_default_colors() { - return Nes_Emu::nes_colors; + return quickerNES::Emu::nes_colors; } -EXPORT int qn_get_joypad_read_count(Nes_Emu *e) +EXPORT int qn_get_joypad_read_count(quickerNES::Emu *e) { - return e->frame().joypad_read_count; + return e->get_joypad_read_count(); } -EXPORT void qn_get_audio_info(Nes_Emu *e, int *sample_count, int *chan_count) +EXPORT void qn_get_audio_info(quickerNES::Emu *e, int *sample_count, int *chan_count) { if (sample_count) *sample_count = e->frame().sample_count; @@ -117,96 +99,75 @@ EXPORT void qn_get_audio_info(Nes_Emu *e, int *sample_count, int *chan_count) *chan_count = e->frame().chan_count; } -EXPORT int qn_read_audio(Nes_Emu *e, short *dest, int max_samples) +EXPORT int qn_read_audio(quickerNES::Emu *e, short *dest, int max_samples) { return e->read_samples(dest, max_samples); } -EXPORT void qn_reset(Nes_Emu *e, int hard) +EXPORT void qn_reset(quickerNES::Emu *e, int hard) { e->reset(hard); } -EXPORT const char *qn_state_size(Nes_Emu *e, int *size) +EXPORT const char *qn_state_size(quickerNES::Emu *e, int *size) { - Sim_Writer w; - Auto_File_Writer a(w); - const char *ret = e->save_state(a); - if (size) - *size = w.size(); - return ret; + jaffarCommon::serializer::Contiguous s; + e->serializeState(s); + *size = s.getOutputSize(); + return 0; } -EXPORT const char *qn_state_save(Nes_Emu *e, void *dest, int size) +EXPORT const char *qn_state_save(quickerNES::Emu *e, void *dest, int size) { - Mem_Writer w(dest, size, 0); - Auto_File_Writer a(w); - const char *ret = e->save_state(a); - if (!ret && w.size() != size) - return "Buffer Underrun!"; - return ret; + jaffarCommon::serializer::Contiguous s(dest, size); + e->serializeState(s); + return 0; } -EXPORT const char *qn_state_load(Nes_Emu *e, const void *src, int size) +EXPORT const char *qn_state_load(quickerNES::Emu *e, const void *src, int size) { - Mem_File_Reader r(src, size); - Auto_File_Reader a(r); - return e->load_state(a); + jaffarCommon::deserializer::Contiguous d(src, size); + e->deserializeState(d); + return 0; } -EXPORT int qn_has_battery_ram(Nes_Emu *e) +EXPORT int qn_has_battery_ram(quickerNES::Emu *e) { return e->has_battery_ram(); } -EXPORT const char *qn_battery_ram_size(Nes_Emu *e, int *size) +EXPORT const char *qn_battery_ram_size(quickerNES::Emu *e, int *size) { - Sim_Writer w; - Auto_File_Writer a(w); - const char *ret = e->save_battery_ram(a); - if (size) - *size = w.size(); - return ret; + *size = e->get_high_mem_size(); + return 0; } -EXPORT const char *qn_battery_ram_save(Nes_Emu *e, void *dest, int size) +EXPORT const char *qn_battery_ram_save(quickerNES::Emu *e, void *dest, int size) { - Mem_Writer w(dest, size, 0); - Auto_File_Writer a(w); - const char *ret = e->save_battery_ram(a); - if (!ret && w.size() != size) - return "Buffer Underrun!"; - return ret; + memcpy(dest, e->high_mem(), size); + return 0; } -EXPORT const char *qn_battery_ram_load(Nes_Emu *e, const void *src, int size) +EXPORT const char *qn_battery_ram_load(quickerNES::Emu *e, const void *src, int size) { - Mem_File_Reader r(src, size); - Auto_File_Reader a(r); - return e->load_battery_ram(a); + memcpy(e->high_mem(), src, size); + return 0; } -EXPORT const char *qn_battery_ram_clear(Nes_Emu *e) +EXPORT const char *qn_battery_ram_clear(quickerNES::Emu *e) { int size = 0; - const char *ret = qn_battery_ram_size(e, &size); - if (ret) - return ret; - void *data = std::malloc(size); - if (!data) - return "Out of Memory!"; - std::memset(data, 0xff, size); - ret = qn_battery_ram_load(e, data, size); - std::free(data); - return ret; + qn_battery_ram_size(e, &size); + std::memset(e->high_mem(), 0xff, size); + return 0; } -EXPORT void qn_set_sprite_limit(Nes_Emu *e, int n) +EXPORT void qn_set_sprite_limit(quickerNES::Emu *e, int n) { - e->set_sprite_mode((Nes_Emu::sprite_mode_t)n); + e->set_sprite_mode((quickerNES::Emu::sprite_mode_t)n); } -EXPORT int qn_get_memory_area(Nes_Emu *e, int which, const void **data, int *size, int *writable, const char **name) +EXPORT int qn_get_memory_area(quickerNES::Emu *e, int which, const void **data, int *size, int *writable, const char **name) { if (!data || !size || !writable || !name) return 0; @@ -215,7 +176,7 @@ EXPORT int qn_get_memory_area(Nes_Emu *e, int which, const void **data, int *siz default: return 0; case 0: - *data = e->low_mem(); + *data = e->get_low_mem(); *size = e->low_mem_size; *writable = 1; *name = "RAM"; @@ -229,58 +190,58 @@ EXPORT int qn_get_memory_area(Nes_Emu *e, int which, const void **data, int *siz case 2: *data = e->chr_mem(); *size = e->chr_size(); - *writable = 0; + *writable = 1; *name = "CHR"; return 1; case 3: *data = e->nametable_mem(); *size = e->nametable_size(); - *writable = 0; + *writable = 1; *name = "CIRAM (nametables)"; return 1; case 4: *data = e->cart()->prg(); *size = e->cart()->prg_size(); - *writable = 0; + *writable = 1; *name = "PRG ROM"; return 1; case 5: *data = e->cart()->chr(); *size = e->cart()->chr_size(); - *writable = 0; + *writable = 1; *name = "CHR VROM"; return 1; case 6: *data = e->pal_mem(); - *size = 32; + *size = e->pal_mem_size(); *writable = 1; *name = "PALRAM"; return 1; case 7: - *data = e->oam_mem(); - *size = 256; + *data = e->spr_mem(); + *size = e->spr_mem_size(); *writable = 1; *name = "OAM"; return 1; } } -EXPORT unsigned char qn_peek_prgbus(Nes_Emu *e, int addr) +EXPORT unsigned char qn_peek_prgbus(quickerNES::Emu *e, int addr) { return e->peek_prg(addr & 0xffff); } -EXPORT void qn_poke_prgbus(Nes_Emu *e, int addr, unsigned char val) +EXPORT void qn_poke_prgbus(quickerNES::Emu *e, int addr, unsigned char val) { e->poke_prg(addr & 0xffff, val); } -EXPORT void qn_get_cpuregs(Nes_Emu *e, unsigned int *dest) +EXPORT void qn_get_cpuregs(quickerNES::Emu *e, unsigned int *dest) { e->get_regs(dest); } -EXPORT const char *qn_get_mapper(Nes_Emu *e, int *number) +EXPORT const char *qn_get_mapper(quickerNES::Emu *e, int *number) { int m = e->cart()->mapper_code(); if (number) @@ -288,54 +249,90 @@ EXPORT const char *qn_get_mapper(Nes_Emu *e, int *number) switch (m) { default: return "unknown"; - case 0: return "nrom"; - case 1: return "mmc1"; - case 2: return "unrom"; - case 3: return "cnrom"; - case 4: return "mmc3"; - case 7: return "aorom"; - case 69: return "fme7"; - case 5: return "mmc5"; - case 19: return "namco106"; - case 24: return "vrc6a"; - case 26: return "vrc6b"; - case 11: return "color_dreams"; - case 34: return "nina1"; - case 66: return "gnrom"; - case 87: return "mapper_87"; + case 0: return "nrom"; + case 1: return "mmc1"; + case 2: return "unrom"; + case 3: return "cnrom"; + case 4: return "mmc3"; + case 5: return "mmc5"; + case 7: return "aorom"; + case 9: return "mmc2"; + case 10: return "mmc4"; + case 11: return "color_dreams"; + case 15: return "k1029/30P"; + case 19: return "namco106"; + case 21: return "vrc2,vrc4(21)"; + case 22: return "vrc2,vrc4(22)"; + case 23: return "vrc2,vrc4(23)"; + case 24: return "vrc6a"; + case 25: return "vrc2,vrc4(25)"; + case 26: return "vrc6b"; + case 30: return "Unrom512"; + case 32: return "Irem_G101"; + case 33: return "TaitoTC0190"; + case 34: return "nina1"; + case 60: return "NROM-128"; + case 66: return "gnrom"; + case 69: return "fme7"; + case 70: return "74x161x162x32(70)"; + case 71: return "camerica"; + case 73: return "vrc3"; + case 75: return "vrc1"; + case 78: return "mapper_78"; + case 79: return "nina03,nina06(79)"; + case 85: return "vrc7"; + case 86: return "mapper_86"; + case 87: return "mapper_87"; + case 88: return "namco34(88)"; + case 89: return "sunsoft2b"; + case 93: return "sunsoft2a"; + case 94: return "Un1rom"; + case 97: return "irem_tam_s1"; + case 113: return "nina03,nina06(113)"; + case 140: return "jaleco_jf11"; + case 152: return "74x161x162x32(152)"; + case 154: return "namco34(154)"; + case 156: return "dis23c01_daou"; + case 180: return "uxrom(inverted)"; + case 184: return "sunsoft1"; + case 190: return "magickidgoogoo"; + case 193: return "tc112"; + case 206: return "namco34(206)"; + case 207: return "taitox1005"; case 232: return "quattro"; - case 9: return "mmc2"; - case 10: return "mmc4"; + case 240: return "mapper_240"; + case 241: return "mapper_241"; + case 246: return "mapper_246"; } } -EXPORT byte qn_get_reg2000(Nes_Emu *e) +EXPORT uint8_t qn_get_reg2000(quickerNES::Emu *e) { return e->get_ppu2000(); } -EXPORT byte *qn_get_palmem(Nes_Emu *e) +EXPORT uint8_t *qn_get_palmem(quickerNES::Emu *e) { return e->pal_mem(); } -EXPORT byte *qn_get_oammem(Nes_Emu *e) +EXPORT uint8_t *qn_get_oammem(quickerNES::Emu *e) { - return e->oam_mem(); + return e->pal_mem(); } -EXPORT byte qn_peek_ppu(Nes_Emu *e, int addr) +EXPORT uint8_t qn_peek_ppu(quickerNES::Emu *e, int addr) { return e->peek_ppu(addr); } -EXPORT void qn_peek_ppubus(Nes_Emu *e, byte *dest) +EXPORT void qn_peek_ppubus(quickerNES::Emu *e, uint8_t *dest) { for (int i = 0; i < 0x3000; i++) dest[i] = e->peek_ppu(i); } -EXPORT void qn_set_tracecb(Nes_Emu *e, void (*cb)(unsigned int *dest)) +EXPORT void qn_set_tracecb(quickerNES::Emu *e, void (*cb)(unsigned int *dest)) { e->set_tracecb(cb); } diff --git a/quicknes/core b/quicknes/core new file mode 160000 index 0000000000..f70db77e69 --- /dev/null +++ b/quicknes/core @@ -0,0 +1 @@ +Subproject commit f70db77e696a57e4d9ce790d610f2de7c0acbf68 diff --git a/quicknes/fex/Data_Reader.cpp b/quicknes/fex/Data_Reader.cpp deleted file mode 100644 index b930dc6d91..0000000000 --- a/quicknes/fex/Data_Reader.cpp +++ /dev/null @@ -1,775 +0,0 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Data_Reader.h" - -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2005-2009 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// Data_Reader - -blargg_err_t Data_Reader::read( void* p, int n ) -{ - assert( n >= 0 ); - - if ( n < 0 ) - return blargg_err_caller; - - if ( n <= 0 ) - return blargg_ok; - - if ( n > remain() ) - return blargg_err_file_eof; - - blargg_err_t err = read_v( p, n ); - if ( !err ) - remain_ -= n; - - return err; -} - -blargg_err_t Data_Reader::read_avail( void* p, int* n_ ) -{ - assert( *n_ >= 0 ); - - int n = min( (BOOST::uint64_t)(*n_), remain() ); - *n_ = 0; - - if ( n < 0 ) - return blargg_err_caller; - - if ( n <= 0 ) - return blargg_ok; - - blargg_err_t err = read_v( p, n ); - if ( !err ) - { - remain_ -= n; - *n_ = n; - } - - return err; -} - -blargg_err_t Data_Reader::read_avail( void* p, long* n ) -{ - int i = STATIC_CAST(int, *n); - blargg_err_t err = read_avail( p, &i ); - *n = i; - return err; -} - -blargg_err_t Data_Reader::skip_v( int count ) -{ - char buf [512]; - while ( count ) - { - int n = min( count, (int) sizeof buf ); - count -= n; - RETURN_ERR( read_v( buf, n ) ); - } - return blargg_ok; -} - -blargg_err_t Data_Reader::skip( int n ) -{ - assert( n >= 0 ); - - if ( n < 0 ) - return blargg_err_caller; - - if ( n <= 0 ) - return blargg_ok; - - if ( n > remain() ) - return blargg_err_file_eof; - - blargg_err_t err = skip_v( n ); - if ( !err ) - remain_ -= n; - - return err; -} - - -// File_Reader - -blargg_err_t File_Reader::seek( BOOST::uint64_t n ) -{ - assert( n >= 0 ); - - if ( n < 0 ) - return blargg_err_caller; - - if ( n == tell() ) - return blargg_ok; - - if ( n > size() ) - return blargg_err_file_eof; - - blargg_err_t err = seek_v( n ); - if ( !err ) - set_tell( n ); - - return err; -} - -blargg_err_t File_Reader::skip_v( BOOST::uint64_t n ) -{ - return seek_v( tell() + n ); -} - - -// Subset_Reader - -Subset_Reader::Subset_Reader( Data_Reader* dr, BOOST::uint64_t size ) : - in( dr ) -{ - set_remain( min( size, dr->remain() ) ); -} - -blargg_err_t Subset_Reader::read_v( void* p, int s ) -{ - return in->read( p, s ); -} - - -// Remaining_Reader - -Remaining_Reader::Remaining_Reader( void const* h, int size, Data_Reader* r ) : - in( r ) -{ - header = h; - header_remain = size; - - set_remain( size + r->remain() ); -} - -blargg_err_t Remaining_Reader::read_v( void* out, int count ) -{ - int first = min( count, header_remain ); - if ( first ) - { - memcpy( out, header, first ); - header = STATIC_CAST(char const*, header) + first; - header_remain -= first; - } - - return in->read( STATIC_CAST(char*, out) + first, count - first ); -} - - -// Mem_File_Reader - -Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : - begin( STATIC_CAST(const char*, p) ) -{ - set_size( s ); -} - -blargg_err_t Mem_File_Reader::read_v( void* p, int s ) -{ - memcpy( p, begin + tell(), s ); - return blargg_ok; -} - -blargg_err_t Mem_File_Reader::seek_v( int ) -{ - return blargg_ok; -} - - -// Callback_Reader - -Callback_Reader::Callback_Reader( callback_t c, BOOST::uint64_t s, void* d ) : - callback( c ), - user_data( d ) -{ - set_remain( s ); -} - -blargg_err_t Callback_Reader::read_v( void* out, int count ) -{ - return callback( user_data, out, count ); -} - - -// Callback_File_Reader - -Callback_File_Reader::Callback_File_Reader( callback_t c, BOOST::uint64_t s, void* d ) : - callback( c ), - user_data( d ) -{ - set_size( s ); -} - -blargg_err_t Callback_File_Reader::read_v( void* out, int count ) -{ - return callback( user_data, out, count, tell() ); -} - -blargg_err_t Callback_File_Reader::seek_v( int ) -{ - return blargg_ok; -} - -static const BOOST::uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE}; - -static const BOOST::uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC}; - -size_t utf8_char_len_from_header( char p_c ) -{ - // what is this? - // BOOST::uint8_t c = (BOOST::uint8_t)p_c; - - size_t cnt = 0; - for(;;) - { - if ( ( p_c & mask_tab[cnt] ) == val_tab[cnt] ) break; - if ( ++cnt >= 6 ) return 0; - } - - return cnt + 1; -} - -size_t utf8_decode_char( const char *p_utf8, unsigned & wide, size_t mmax ) -{ - const BOOST::uint8_t * utf8 = ( const BOOST::uint8_t* )p_utf8; - - if ( mmax == 0 ) - { - wide = 0; - return 0; - } - - if ( utf8[0] < 0x80 ) - { - wide = utf8[0]; - return utf8[0]>0 ? 1 : 0; - } - if ( mmax > 6 ) mmax = 6; - wide = 0; - - unsigned res=0; - unsigned n; - unsigned cnt=0; - for(;;) - { - if ( ( *utf8 & mask_tab[cnt] ) == val_tab[cnt] ) break; - if ( ++cnt >= mmax ) return 0; - } - cnt++; - - if ( cnt==2 && !( *utf8 & 0x1E ) ) return 0; - - if ( cnt == 1 ) - res = *utf8; - else - res = ( 0xFF >> ( cnt + 1 ) ) & *utf8; - - for ( n = 1; n < cnt; n++ ) - { - if ( ( utf8[n] & 0xC0 ) != 0x80 ) - return 0; - if ( !res && n == 2 && !( ( utf8[n] & 0x7F ) >> ( 7 - cnt ) ) ) - return 0; - - res = ( res << 6 ) | ( utf8[n] & 0x3F ); - } - - wide = res; - - return cnt; -} - -size_t utf8_encode_char( unsigned wide, char * target ) -{ - size_t count; - - if ( wide < 0x80 ) - count = 1; - else if ( wide < 0x800 ) - count = 2; - else if ( wide < 0x10000 ) - count = 3; - else if ( wide < 0x200000 ) - count = 4; - else if ( wide < 0x4000000 ) - count = 5; - else if ( wide <= 0x7FFFFFFF ) - count = 6; - else - return 0; - - if ( target == 0 ) - return count; - - switch ( count ) - { - case 6: - target[5] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x4000000; - case 5: - target[4] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x200000; - case 4: - target[3] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x10000; - case 3: - target[2] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x800; - case 2: - target[1] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0xC0; - case 1: - target[0] = wide; - } - - return count; -} - -size_t utf16_encode_char( unsigned cur_wchar, blargg_wchar_t * out ) -{ - if ( cur_wchar < 0x10000 ) - { - if ( out ) *out = (blargg_wchar_t) cur_wchar; return 1; - } - else if ( cur_wchar < ( 1 << 20 ) ) - { - unsigned c = cur_wchar - 0x10000; - //MSDN: - //The first (high) surrogate is a 16-bit code value in the range U+D800 to U+DBFF. The second (low) surrogate is a 16-bit code value in the range U+DC00 to U+DFFF. Using surrogates, Unicode can support over one million characters. For more details about surrogates, refer to The Unicode Standard, version 2.0. - if ( out ) - { - out[0] = ( blargg_wchar_t )( 0xD800 | ( 0x3FF & ( c >> 10 ) ) ); - out[1] = ( blargg_wchar_t )( 0xDC00 | ( 0x3FF & c ) ) ; - } - return 2; - } - else - { - if ( out ) *out = '?'; return 1; - } -} - -size_t utf16_decode_char( const blargg_wchar_t * p_source, unsigned * p_out, size_t p_source_length ) -{ - if ( p_source_length == 0 ) return 0; - else if ( p_source_length == 1 ) - { - *p_out = p_source[0]; - return 1; - } - else - { - size_t retval = 0; - unsigned decoded = p_source[0]; - if ( decoded != 0 ) - { - retval = 1; - if ( ( decoded & 0xFC00 ) == 0xD800 ) - { - unsigned low = p_source[1]; - if ( ( low & 0xFC00 ) == 0xDC00 ) - { - decoded = 0x10000 + ( ( ( decoded & 0x3FF ) << 10 ) | ( low & 0x3FF ) ); - retval = 2; - } - } - } - *p_out = decoded; - return retval; - } -} - -// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows. -char* blargg_to_utf8( const blargg_wchar_t* wpath ) -{ - if ( wpath == NULL ) - return NULL; - - size_t needed = 0; - size_t mmax = blargg_wcslen( wpath ); - if ( mmax <= 0 ) - return NULL; - - size_t ptr = 0; - while ( ptr < mmax ) - { - unsigned wide = 0; - size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - needed += utf8_encode_char( wide, 0 ); - } - if ( needed <= 0 ) - return NULL; - - char* path = (char*) calloc( needed + 1, 1 ); - if ( path == NULL ) - return NULL; - - ptr = 0; - size_t actual = 0; - while ( ptr < mmax && actual < needed ) - { - unsigned wide = 0; - size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - actual += utf8_encode_char( wide, path + actual ); - } - - if ( actual == 0 ) - { - free( path ); - return NULL; - } - - assert( actual == needed ); - return path; -} - -// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows. -blargg_wchar_t* blargg_to_wide( const char* path ) -{ - if ( path == NULL ) - return NULL; - - size_t mmax = strlen( path ); - if ( mmax <= 0 ) - return NULL; - - size_t needed = 0; - size_t ptr = 0; - while ( ptr < mmax ) - { - unsigned wide = 0; - size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - needed += utf16_encode_char( wide, 0 ); - } - if ( needed <= 0 ) - return NULL; - - blargg_wchar_t* wpath = (blargg_wchar_t*) calloc( needed + 1, sizeof *wpath ); - if ( wpath == NULL ) - return NULL; - - ptr = 0; - size_t actual = 0; - while ( ptr < mmax && actual < needed ) - { - unsigned wide = 0; - size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - actual += utf16_encode_char( wide, wpath + actual ); - } - if ( actual == 0 ) - { - free( wpath ); - return NULL; - } - - assert( actual == needed ); - return wpath; -} - -#ifdef _WIN32 - -static FILE* blargg_fopen( const char path [], const char mode [] ) -{ - FILE* file = NULL; - blargg_wchar_t* wmode = NULL; - blargg_wchar_t* wpath = NULL; - - wpath = blargg_to_wide( path ); - if ( wpath ) - { - wmode = blargg_to_wide( mode ); - if ( wmode ) - file = _wfopen( wpath, wmode ); - } - - // Save and restore errno in case free() clears it - int saved_errno = errno; - free( wmode ); - free( wpath ); - errno = saved_errno; - - return file; -} - -#else - -static inline FILE* blargg_fopen( const char path [], const char mode [] ) -{ - return fopen( path, mode ); -} - -#endif - - -// Std_File_Reader - -Std_File_Reader::Std_File_Reader() -{ - file_ = NULL; -} - -Std_File_Reader::~Std_File_Reader() -{ - close(); -} - -static blargg_err_t blargg_fopen( FILE** out, const char path [] ) -{ - errno = 0; - *out = blargg_fopen( path, "rb" ); - if ( !*out ) - { - #ifdef ENOENT - if ( errno == ENOENT ) - return blargg_err_file_missing; - #endif - #ifdef ENOMEM - if ( errno == ENOMEM ) - return blargg_err_memory; - #endif - return blargg_err_file_read; - } - - return blargg_ok; -} - -static blargg_err_t blargg_fsize( FILE* f, long* out ) -{ - if ( fseek( f, 0, SEEK_END ) ) - return blargg_err_file_io; - - *out = ftell( f ); - if ( *out < 0 ) - return blargg_err_file_io; - - if ( fseek( f, 0, SEEK_SET ) ) - return blargg_err_file_io; - - return blargg_ok; -} - -blargg_err_t Std_File_Reader::open( const char path [] ) -{ - close(); - - FILE* f; - RETURN_ERR( blargg_fopen( &f, path ) ); - - long s; - blargg_err_t err = blargg_fsize( f, &s ); - if ( err ) - { - fclose( f ); - return err; - } - - file_ = f; - set_size( s ); - - return blargg_ok; -} - -void Std_File_Reader::make_unbuffered() -{ - if ( setvbuf( STATIC_CAST(FILE*, file_), NULL, _IONBF, 0 ) ) - check( false ); // shouldn't fail, but OK if it does -} - -blargg_err_t Std_File_Reader::read_v( void* p, int s ) -{ - if ( (size_t) s != fread( p, 1, s, STATIC_CAST(FILE*, file_) ) ) - { - // Data_Reader's wrapper should prevent EOF - check( !feof( STATIC_CAST(FILE*, file_) ) ); - - return blargg_err_file_io; - } - - return blargg_ok; -} - -#ifdef __CELLOS_LV2__ -int -fseeko(FILE *stream, off_t pos, int whence) -{ - return fseek(stream, (long)pos, whence); -} -#endif - -blargg_err_t Std_File_Reader::seek_v( BOOST::uint64_t n ) -{ -#ifdef _WIN32 - if ( fseek( STATIC_CAST(FILE*, file_), n, SEEK_SET ) ) -#else - if ( fseeko( STATIC_CAST(FILE*, file_), n, SEEK_SET ) ) -#endif - { - // Data_Reader's wrapper should prevent EOF - check( !feof( STATIC_CAST(FILE*, file_) ) ); - - return blargg_err_file_io; - } - - return blargg_ok; -} - -void Std_File_Reader::close() -{ - if ( file_ ) - { - fclose( STATIC_CAST(FILE*, file_) ); - file_ = NULL; - } -} - - -// Gzip_File_Reader - -#ifndef __LIBRETRO__ -#ifdef HAVE_ZLIB_H - -#include "zlib.h" - -static const char* get_gzip_eof( const char path [], long* eof ) -{ - FILE* file; - RETURN_ERR( blargg_fopen( &file, path ) ); - - int const h_size = 4; - unsigned char h [h_size]; - - // read four bytes to ensure that we can seek to -4 later - if ( fread( h, 1, h_size, file ) != (size_t) h_size || h[0] != 0x1F || h[1] != 0x8B ) - { - // Not gzipped - if ( ferror( file ) ) - return blargg_err_file_io; - - if ( fseek( file, 0, SEEK_END ) ) - return blargg_err_file_io; - - *eof = ftell( file ); - if ( *eof < 0 ) - return blargg_err_file_io; - } - else - { - // Gzipped; get uncompressed size from end - if ( fseek( file, -h_size, SEEK_END ) ) - return blargg_err_file_io; - - if ( fread( h, 1, h_size, file ) != (size_t) h_size ) - return blargg_err_file_io; - - *eof = get_le32( h ); - } - - if ( fclose( file ) ) - check( false ); - - return blargg_ok; -} - -Gzip_File_Reader::Gzip_File_Reader() -{ - file_ = NULL; -} - -Gzip_File_Reader::~Gzip_File_Reader() -{ - close(); -} - -blargg_err_t Gzip_File_Reader::open( const char path [] ) -{ - close(); - - long s; - RETURN_ERR( get_gzip_eof( path, &s ) ); - - file_ = gzopen( path, "rb" ); - if ( !file_ ) - return blargg_err_file_read; - - set_size( s ); - return blargg_ok; -} - -static blargg_err_t convert_gz_error( gzFile file ) -{ - int err; - gzerror( file, &err ); - - switch ( err ) - { - case Z_STREAM_ERROR: break; - case Z_DATA_ERROR: return blargg_err_file_corrupt; - case Z_MEM_ERROR: return blargg_err_memory; - case Z_BUF_ERROR: break; - } - return blargg_err_internal; -} - -blargg_err_t Gzip_File_Reader::read_v( void* p, int s ) -{ - int result = gzread( (gzFile) file_, p, s ); - if ( result != s ) - { - if ( result < 0 ) - return convert_gz_error( (gzFile) file_ ); - - return blargg_err_file_corrupt; - } - - return blargg_ok; -} - -blargg_err_t Gzip_File_Reader::seek_v( int n ) -{ - if ( gzseek( (gzFile) file_, n, SEEK_SET ) < 0 ) - return convert_gz_error( (gzFile) file_ ); - - return blargg_ok; -} - -void Gzip_File_Reader::close() -{ - if ( file_ ) - { - if ( gzclose( (gzFile) file_ ) ) - check( false ); - file_ = NULL; - } -} -#endif - -#endif diff --git a/quicknes/fex/Data_Reader.h b/quicknes/fex/Data_Reader.h deleted file mode 100644 index 1cf92bd139..0000000000 --- a/quicknes/fex/Data_Reader.h +++ /dev/null @@ -1,265 +0,0 @@ -// Lightweight interface for reading data from byte stream - -// File_Extractor 1.0.0 -#ifndef DATA_READER_H -#define DATA_READER_H - -#include "blargg_common.h" - -/* Some functions accept a long instead of int for convenience where caller has -a long due to some other interface, and would otherwise have to get a warning, -or cast it (and verify that it wasn't outside the range of an int). - -To really support huge (>2GB) files, long isn't a solution, since there's no -guarantee it's more than 32 bits. We'd need to use long long (if available), or -something compiler-specific, and change all places file sizes or offsets are -used. */ - -// Supports reading and finding out how many bytes are remaining -class Data_Reader { -public: - - // Reads min(*n,remain()) bytes and sets *n to this number, thus trying to read more - // tham remain() bytes doesn't result in error, just *n being set to remain(). - blargg_err_t read_avail( void* p, int* n ); - blargg_err_t read_avail( void* p, long* n ); - - // Reads exactly n bytes, or returns error if they couldn't ALL be read. - // Reading past end of file results in blargg_err_file_eof. - blargg_err_t read( void* p, int n ); - - // Number of bytes remaining until end of file - BOOST::uint64_t remain() const { return remain_; } - - // Reads and discards n bytes. Skipping past end of file results in blargg_err_file_eof. - blargg_err_t skip( int n ); - - virtual ~Data_Reader() { } - -private: - // noncopyable - Data_Reader( const Data_Reader& ); - Data_Reader& operator = ( const Data_Reader& ); - -// Derived interface -protected: - Data_Reader() : remain_( 0 ) { } - - // Sets remain - void set_remain( BOOST::uint64_t n ) { assert( n >= 0 ); remain_ = n; } - - // Do same as read(). Guaranteed that 0 < n <= remain(). Value of remain() is updated - // AFTER this call succeeds, not before. set_remain() should NOT be called from this. - virtual blargg_err_t read_v( void*, int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) - - // Do same as skip(). Guaranteed that 0 < n <= remain(). Default just reads data - // and discards it. Value of remain() is updated AFTER this call succeeds, not - // before. set_remain() should NOT be called from this. - virtual blargg_err_t skip_v( int n ); - -// Implementation -public: - BLARGG_DISABLE_NOTHROW - -private: - BOOST::uint64_t remain_; -}; - - -// Supports seeking in addition to Data_Reader operations -class File_Reader : public Data_Reader { -public: - - // Size of file - BOOST::uint64_t size() const { return size_; } - - // Current position in file - BOOST::uint64_t tell() const { return size_ - remain(); } - - // Goes to new position - blargg_err_t seek( BOOST::uint64_t ); - -// Derived interface -protected: - // Sets size and resets position - void set_size( BOOST::uint64_t n ) { size_ = n; Data_Reader::set_remain( n ); } - void set_size( int n ) { set_size( STATIC_CAST(BOOST::uint64_t, n) ); } - void set_size( long n ) { set_size( STATIC_CAST(BOOST::uint64_t, n) ); } - - // Sets reported position - void set_tell( BOOST::uint64_t i ) { assert( 0 <= i && i <= size_ ); Data_Reader::set_remain( size_ - i ); } - - // Do same as seek(). Guaranteed that 0 <= n <= size(). Value of tell() is updated - // AFTER this call succeeds, not before. set_* functions should NOT be called from this. - virtual blargg_err_t seek_v( BOOST::uint64_t n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) - -// Implementation -protected: - File_Reader() : size_( 0 ) { } - - virtual blargg_err_t skip_v( BOOST::uint64_t ); - -private: - BOOST::uint64_t size_; - - void set_remain(); // avoid accidental use of set_remain -}; - - -// Reads from file on disk -class Std_File_Reader : public File_Reader { -public: - - // Opens file - blargg_err_t open( const char path [] ); - - // Closes file if one was open - void close(); - - // Switches to unbuffered mode. Useful if buffering is already being - // done at a higher level. - void make_unbuffered(); - -// Implementation -public: - Std_File_Reader(); - virtual ~Std_File_Reader(); - -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( BOOST::uint64_t ); - -private: - void* file_; -}; - - -// Treats range of memory as a file -class Mem_File_Reader : public File_Reader { -public: - - Mem_File_Reader( const void* begin, long size ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - const char* const begin; -}; - - -// Allows only count bytes to be read from reader passed -class Subset_Reader : public Data_Reader { -public: - - Subset_Reader( Data_Reader*, BOOST::uint64_t count ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - Data_Reader* const in; -}; - - -// Joins already-read header and remaining data into original file. -// Meant for cases where you've already read header and don't want -// to seek and re-read data (for efficiency). -class Remaining_Reader : public Data_Reader { -public: - - Remaining_Reader( void const* header, int header_size, Data_Reader* ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - Data_Reader* const in; - void const* header; - int header_remain; -}; - - -// Invokes callback function to read data -extern "C" { // necessary to be usable from C - typedef const char* (*callback_reader_func_t)( - void* user_data, // Same value passed to constructor - void* out, // Buffer to place data into - int count // Number of bytes to read - ); -} -class Callback_Reader : public Data_Reader { -public: - typedef callback_reader_func_t callback_t; - Callback_Reader( callback_t, BOOST::uint64_t size, void* user_data ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - callback_t const callback; - void* const user_data; -}; - - -// Invokes callback function to read data -extern "C" { // necessary to be usable from C - typedef const char* (*callback_file_reader_func_t)( - void* user_data, // Same value passed to constructor - void* out, // Buffer to place data into - int count, // Number of bytes to read - BOOST::uint64_t pos // Position in file to read from - ); -} -class Callback_File_Reader : public File_Reader { -public: - typedef callback_file_reader_func_t callback_t; - Callback_File_Reader( callback_t, BOOST::uint64_t size, void* user_data ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - callback_t const callback; - void* const user_data; -}; - - -#ifdef HAVE_ZLIB_H - -// Reads file compressed with gzip (or uncompressed) -class Gzip_File_Reader : public File_Reader { -public: - - // Opens possibly gzipped file - blargg_err_t open( const char path [] ); - - // Closes file if one was open - void close(); - -// Implementation -public: - Gzip_File_Reader(); - ~Gzip_File_Reader(); - -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - // void* so "zlib.h" doesn't have to be included here - void* file_; -}; -#endif - -char* blargg_to_utf8( const blargg_wchar_t* ); -blargg_wchar_t* blargg_to_wide( const char* ); - -#endif diff --git a/quicknes/fex/blargg_common.cpp b/quicknes/fex/blargg_common.cpp deleted file mode 100644 index ab86e64573..0000000000 --- a/quicknes/fex/blargg_common.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "blargg_common.h" - -/* Copyright (C) 2008-2009 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void blargg_vector_::init() -{ - begin_ = NULL; - size_ = 0; -} - -void blargg_vector_::clear() -{ - void* p = begin_; - begin_ = NULL; - size_ = 0; - free( p ); -} - -blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size ) -{ - if ( n != size_ ) - { - if ( n == 0 ) - { - // Simpler to handle explicitly. Realloc will handle a size of 0, - // but then we have to avoid raising an error for a NULL return. - clear(); - } - else - { - void* p = realloc( begin_, n * elem_size ); - CHECK_ALLOC( p ); - begin_ = p; - size_ = n; - } - } - return blargg_ok; -} diff --git a/quicknes/fex/blargg_common.h b/quicknes/fex/blargg_common.h deleted file mode 100644 index ac29f929cd..0000000000 --- a/quicknes/fex/blargg_common.h +++ /dev/null @@ -1,217 +0,0 @@ -// Sets up common environment for Shay Green's libraries. -// To change configuration options, modify blargg_config.h, not this file. - -// File_Extractor 1.0.0 -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -#include -#include -#include -#include - -typedef const char* blargg_err_t; // 0 on success, otherwise error string - -#ifdef _WIN32 -typedef wchar_t blargg_wchar_t; -#else -typedef uint16_t blargg_wchar_t; -#endif - -inline size_t blargg_wcslen( const blargg_wchar_t* str ) -{ - size_t length = 0; - while ( *str++ ) length++; - return length; -} - -// Success; no error -blargg_err_t const blargg_ok = 0; - -// BLARGG_RESTRICT: equivalent to C99's restrict, where supported -#if __GNUC__ >= 3 || _MSC_VER >= 1100 - #define BLARGG_RESTRICT __restrict -#else - #define BLARGG_RESTRICT -#endif - -#if __cplusplus >= 199711 - #define BLARGG_MUTABLE mutable -#else - #define BLARGG_MUTABLE -#endif - -/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). -I don't just use 'abcd' because that's implementation-dependent. */ -#define BLARGG_4CHAR( a, b, c, d ) \ - ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) - -/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -Can be used at file, function, or class scope. */ -#ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified - #define BLARGG_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) -#else - // Others fail when declaring same function multiple times in class, - // so differentiate them by line - #define BLARGG_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) -#endif - -/* Pure virtual functions cause a vtable entry to a "called pure virtual" -error handler, requiring linkage to the C++ runtime library. This macro is -used in place of the "= 0", and simply expands to its argument. During -development, it expands to "= 0", allowing detection of missing overrides. */ -#define BLARGG_PURE( def ) def - -/* My code depends on ASCII anywhere a character or string constant is -compared with data read from a file, and anywhere file data is read and -treated as a string. */ -#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 - #error "ASCII character set required" -#endif - -/* My code depends on int being at least 32 bits. Almost everything these days -uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints -to test with. The issue can't be gotten around by using a suitable blargg_int -everywhere either, because int is often converted to implicitly when doing -arithmetic on smaller types. */ -#if UINT_MAX < 0xFFFFFFFF - #error "int must be at least 32 bits" -#endif - -// In case compiler doesn't support these properly. Used rarely. -#define STATIC_CAST(T,expr) static_cast (expr) -#define CONST_CAST( T,expr) const_cast (expr) - -// User configuration can override the above macros if necessary -#include "blargg_config.h" - -/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a -future version. In GCC, we can let the compiler warn. In other compilers, -we strip it out unless BLARGG_LEGACY is true. */ -#if BLARGG_LEGACY - // Allow old client code to work without warnings - #define BLARGG_DEPRECATED_TEXT( text ) text - #define BLARGG_DEPRECATED( text ) text -#elif __GNUC__ >= 4 - // In GCC, we can mark declarations and let the compiler warn - #define BLARGG_DEPRECATED_TEXT( text ) text - #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text -#else - // By default, deprecated items are removed, to avoid use in new code - #define BLARGG_DEPRECATED_TEXT( text ) - #define BLARGG_DEPRECATED( text ) -#endif - -/* BOOST::int8_t, BOOST::int32_t, etc. -I used BOOST since I originally was going to allow use of the boost library -for prividing the definitions. If I'm defining them, they must be scoped or -else they could conflict with the standard ones at global scope. Even if -HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at -global scope already. */ -#if defined (HAVE_STDINT_H) || \ - UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF - #include - #define BOOST -#else - struct BOOST - { - typedef signed char int8_t; - typedef unsigned char uint8_t; - typedef short int16_t; - typedef unsigned short uint16_t; - typedef int int32_t; - typedef unsigned int uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; - }; -#endif - -/* My code is not written with exceptions in mind, so either uses new (nothrow) -OR overrides operator new in my classes. The former is best since clients -creating objects will get standard exceptions on failure, but that causes it -to require the standard C++ library. So, when the client is using the C -interface, I override operator new to use malloc. */ - -// BLARGG_DISABLE_NOTHROW is put inside classes -#ifndef BLARGG_DISABLE_NOTHROW - // throw spec mandatory in ISO C++ if NULL can be returned - #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 - #define BLARGG_THROWS_NOTHING throw () - #else - #define BLARGG_THROWS_NOTHING - #endif - - #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ - void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } - - #define BLARGG_NEW new -#else - // BLARGG_NEW is used in place of new in library code - #include - #define BLARGG_NEW new (std::nothrow) -#endif - - class blargg_vector_ { - protected: - void* begin_; - size_t size_; - void init(); - blargg_err_t resize_( size_t n, size_t elem_size ); - public: - size_t size() const { return size_; } - void clear(); - }; - -// Very lightweight vector for POD types (no constructor/destructor) -template -class blargg_vector : public blargg_vector_ { - union T_must_be_pod { T t; }; // fails if T is not POD -public: - blargg_vector() { init(); } - ~blargg_vector() { clear(); } - - blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } - - T* begin() { return static_cast (begin_); } - const T* begin() const { return static_cast (begin_); } - - T* end() { return static_cast (begin_) + size_; } - const T* end() const { return static_cast (begin_) + size_; } - - T& operator [] ( size_t n ) - { - assert( n < size_ ); - return static_cast (begin_) [n]; - } - - const T& operator [] ( size_t n ) const - { - assert( n < size_ ); - return static_cast (begin_) [n]; - } -}; - -// Callback function with user data. -// blargg_callback set_callback; // for user, this acts like... -// void set_callback( T func, void* user_data = NULL ); // ...this -// To call function, do set_callback.f( .. set_callback.data ... ); -template -struct blargg_callback -{ - T f; - void* data; - blargg_callback() { f = NULL; } - void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } -}; - -BLARGG_DEPRECATED( typedef signed int blargg_long; ) -BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) -#if BLARGG_LEGACY - #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT -#endif - -#endif diff --git a/quicknes/fex/blargg_config.h b/quicknes/fex/blargg_config.h deleted file mode 100644 index 73d9613f48..0000000000 --- a/quicknes/fex/blargg_config.h +++ /dev/null @@ -1,37 +0,0 @@ -// Library configuration. Modify this file as necessary. - -// File_Extractor 1.0.0 -#ifndef BLARGG_CONFIG_H -#define BLARGG_CONFIG_H - -// Uncomment a #define line below to have effect described. -// #define HAVE_ZLIB_H - -// Enable RAR archive support. Doing so adds extra licensing restrictions -// to this library (see unrar/readme.txt for more information). -#define FEX_ENABLE_RAR 1 - -// Accept file paths encoded as UTF-8. Currently only affects Windows, -// as Unix/Linux/Mac OS X already use UTF-8 paths. -#define BLARGG_UTF8_PATHS 1 - -// Enable support for as building DLL on Windows. -//#define BLARGG_BUILD_DLL 1 - -// Support only the listed archive types. Remove any you don't need. -/* -#define FEX_TYPE_LIST \ - fex_7z_type,\ - fex_gz_type,\ - fex_rar_type,\ - fex_zip_type, -*/ - -#define HAVE_STDINT_H - -// Use standard config.h if present -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#endif diff --git a/quicknes/fex/blargg_endian.h b/quicknes/fex/blargg_endian.h deleted file mode 100644 index d012871f21..0000000000 --- a/quicknes/fex/blargg_endian.h +++ /dev/null @@ -1,185 +0,0 @@ -// CPU Byte Order Utilities - -// File_Extractor 1.0.0 -#ifndef BLARGG_ENDIAN_H -#define BLARGG_ENDIAN_H - -#include "blargg_common.h" - -// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) -#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ - defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - #define BLARGG_CPU_RISC 1 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one may be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else - -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 -#endif - -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#elif !defined (__mips__) - // No endian specified; assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN -#endif - -inline void blargg_verify_byte_order() -{ - #ifndef NDEBUG - #if BLARGG_BIG_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i == 0 ); - #elif BLARGG_LITTLE_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i != 0 ); - #endif - #endif -} - -inline unsigned get_le16( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [1] << 8 | - (unsigned) ((unsigned char const*) p) [0]; -} - -inline unsigned get_be16( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [0] << 8 | - (unsigned) ((unsigned char const*) p) [1]; -} - -inline unsigned get_le32( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [3] << 24 | - (unsigned) ((unsigned char const*) p) [2] << 16 | - (unsigned) ((unsigned char const*) p) [1] << 8 | - (unsigned) ((unsigned char const*) p) [0]; -} - -inline unsigned get_be32( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [0] << 24 | - (unsigned) ((unsigned char const*) p) [1] << 16 | - (unsigned) ((unsigned char const*) p) [2] << 8 | - (unsigned) ((unsigned char const*) p) [3]; -} - -inline void set_le16( void* p, unsigned n ) -{ - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} - -inline void set_be16( void* p, unsigned n ) -{ - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} - -inline void set_le32( void* p, unsigned n ) -{ - ((unsigned char*) p) [0] = (unsigned char) n; - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); -} - -inline void set_be32( void* p, unsigned n ) -{ - ((unsigned char*) p) [3] = (unsigned char) n; - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - - #if BLARGG_CPU_POWERPC - // PowerPC has special byte-reversed instructions - #if defined (__MWERKS__) - #define GET_LE16( addr ) (__lhbrx( addr, 0 )) - #define GET_LE32( addr ) (__lwbrx( addr, 0 )) - #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) - #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) - #elif defined (__GNUC__) - #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) - #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) - #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) - #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) - #endif - #endif - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) -#endif - -#ifndef GET_LE32 - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) -#endif - -#ifndef GET_BE32 - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } -inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } -inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } - -#endif diff --git a/quicknes/fex/blargg_errors.cpp b/quicknes/fex/blargg_errors.cpp deleted file mode 100644 index 025ea50078..0000000000 --- a/quicknes/fex/blargg_errors.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "blargg_errors.h" - -/* Copyright (C) 2009 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC; -blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY; -blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER; -blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL; -blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION; - -blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING; -blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ; -blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE; -blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO; -blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL; -blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF; - -blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE; -blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE; -blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT; - -const char* blargg_err_str( blargg_err_t err ) -{ - if ( !err ) - return ""; - - if ( *err == BLARGG_ERR_TYPE("")[0] ) - return err + 1; - - return err; -} - -bool blargg_is_err_type( blargg_err_t err, const char type [] ) -{ - if ( err ) - { - // True if first strlen(type) characters of err match type - char const* p = err; - while ( *type && *type == *p ) - { - type++; - p++; - } - - if ( !*type ) - return true; - } - - return false; -} - -const char* blargg_err_details( blargg_err_t err ) -{ - const char* p = err; - if ( !p ) - { - p = ""; - } - else if ( *p == BLARGG_ERR_TYPE("")[0] ) - { - while ( *p && *p != ';' ) - p++; - - // Skip ; and space after it - if ( *p ) - { - p++; - - check( *p == ' ' ); - if ( *p ) - p++; - } - } - return p; -} - -int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] ) -{ - if ( !err ) - return 0; - - while ( codes->str && !blargg_is_err_type( err, codes->str ) ) - codes++; - - return codes->code; -} - -blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] ) -{ - if ( !code ) - return blargg_ok; - - while ( codes->str && codes->code != code ) - codes++; - - if ( !codes->str ) - return blargg_err_generic; - - return codes->str; -} diff --git a/quicknes/fex/blargg_errors.h b/quicknes/fex/blargg_errors.h deleted file mode 100644 index 2b6dd3c05f..0000000000 --- a/quicknes/fex/blargg_errors.h +++ /dev/null @@ -1,80 +0,0 @@ -// Error strings and conversion functions - -// File_Extractor 1.0.0 -#ifndef BLARGG_ERRORS_H -#define BLARGG_ERRORS_H - -#ifndef BLARGG_COMMON_H - #include "blargg_common.h" -#endif - -typedef const char blargg_err_def_t []; - -// Basic errors -extern blargg_err_def_t blargg_err_generic; -extern blargg_err_def_t blargg_err_memory; -extern blargg_err_def_t blargg_err_caller; -extern blargg_err_def_t blargg_err_internal; -extern blargg_err_def_t blargg_err_limitation; - -// File low-level -extern blargg_err_def_t blargg_err_file_missing; // not found -extern blargg_err_def_t blargg_err_file_read; -extern blargg_err_def_t blargg_err_file_write; -extern blargg_err_def_t blargg_err_file_io; -extern blargg_err_def_t blargg_err_file_full; -extern blargg_err_def_t blargg_err_file_eof; - -// File high-level -extern blargg_err_def_t blargg_err_file_type; // wrong file type -extern blargg_err_def_t blargg_err_file_feature; -extern blargg_err_def_t blargg_err_file_corrupt; - -// C string describing error, or "" if err == NULL -const char* blargg_err_str( blargg_err_t err ); - -// True iff error is of given type, or false if err == NULL -bool blargg_is_err_type( blargg_err_t, const char type [] ); - -// Details of error without describing main cause, or "" if err == NULL -const char* blargg_err_details( blargg_err_t err ); - -// Converts error string to integer code using mapping table. Calls blargg_is_err_type() -// for each str and returns code on first match. Returns 0 if err == NULL. -struct blargg_err_to_code_t { - const char* str; - int code; -}; -int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] ); - -// Converts error code back to string. If code == 0, returns NULL. If not in table, -// returns blargg_err_generic. -blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] ); - -// Generates error string literal with details of cause -#define BLARGG_ERR( type, str ) (type "; " str) - -// Extra space to make it clear when blargg_err_str() isn't called to get -// printable version of error. At some point, I might prefix error strings -// with a code, to speed conversion to a code. -#define BLARGG_ERR_TYPE( str ) " " str - -// Error types to pass to BLARGG_ERR macro -#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" ) -#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" ) -#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" ) -#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" ) -#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" ) - -#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" ) -#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" ) -#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" ) -#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" ) -#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" ) -#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" ) - -#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" ) -#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" ) -#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" ) - -#endif diff --git a/quicknes/fex/blargg_source.h b/quicknes/fex/blargg_source.h deleted file mode 100644 index cd3065b4fc..0000000000 --- a/quicknes/fex/blargg_source.h +++ /dev/null @@ -1,121 +0,0 @@ -/* Included at the beginning of library source files, AFTER all other #include -lines. Sets up helpful macros and services used in my source code. Since this -is only "active" in my source code, I don't have to worry about polluting the -global namespace with unprefixed names. */ - -// File_Extractor 1.0.0 -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -#ifndef BLARGG_COMMON_H // optimization only - #include "blargg_common.h" -#endif -#include "blargg_errors.h" - -#include /* memcpy(), memset(), memmove() */ -#include /* offsetof() */ - -/* The following four macros are for debugging only. Some or all might be -defined to do nothing, depending on the circumstances. Described is what -happens when a particular macro is defined to do something. When defined to -do nothing, the macros do NOT evaluate their argument(s). */ - -/* If expr is false, prints file and line number, then aborts program. Meant -for checking internal state and consistency. A failed assertion indicates a bug -in MY code. - -void assert( bool expr ); */ -#include - -/* If expr is false, prints file and line number, then aborts program. Meant -for checking caller-supplied parameters and operations that are outside the -control of the module. A failed requirement probably indicates a bug in YOUR -code. - -void require( bool expr ); */ -#undef require -#define require( expr ) assert( expr ) - -/* Like printf() except output goes to debugging console/file. - -void dprintf( const char format [], ... ); */ -static inline void blargg_dprintf_( const char [], ... ) { } -#undef dprintf -#define dprintf (1) ? (void) 0 : blargg_dprintf_ - -/* If expr is false, prints file and line number to debug console/log, then -continues execution normally. Meant for flagging potential problems or things -that should be looked into, but that aren't serious problems. - -void check( bool expr ); */ -#undef check -#define check( expr ) ((void) 0) - -/* If expr yields non-NULL error string, returns it from current function, -otherwise continues normally. */ -#undef RETURN_ERR -#define RETURN_ERR( expr ) \ - do {\ - blargg_err_t blargg_return_err_ = (expr);\ - if ( blargg_return_err_ )\ - return blargg_return_err_;\ - } while ( 0 ) - -/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */ -#undef CHECK_ALLOC -#define CHECK_ALLOC( ptr ) \ - do {\ - if ( !(ptr) )\ - return blargg_err_memory;\ - } while ( 0 ) - -/* The usual min/max functions for built-in types. */ - -template T min( T x, T y ) { return x < y ? x : y; } -template T max( T x, T y ) { return x > y ? x : y; } - -#define BLARGG_DEF_MIN_MAX( type ) - -BLARGG_DEF_MIN_MAX( int ) -BLARGG_DEF_MIN_MAX( unsigned ) -BLARGG_DEF_MIN_MAX( long ) -BLARGG_DEF_MIN_MAX( unsigned long ) -BLARGG_DEF_MIN_MAX( float ) -BLARGG_DEF_MIN_MAX( double ) -#if __WORDSIZE != 64 -BLARGG_DEF_MIN_MAX( BOOST::uint64_t ) -#endif - -// typedef unsigned char byte; -typedef unsigned char blargg_byte; -#undef byte -#define byte blargg_byte - -#ifndef BLARGG_EXPORT - #if defined (_WIN32) && BLARGG_BUILD_DLL - #define BLARGG_EXPORT __declspec(dllexport) - #elif defined (__GNUC__) - // can always set visibility, even when not building DLL - #define BLARGG_EXPORT __attribute__ ((visibility ("default"))) - #else - #define BLARGG_EXPORT - #endif -#endif - -#if BLARGG_LEGACY - #define BLARGG_CHECK_ALLOC CHECK_ALLOC - #define BLARGG_RETURN_ERR RETURN_ERR -#endif - -// Called after failed operation when overall operation may still complete OK. -// Only used by unit testing framework. -#undef ACK_FAILURE -#define ACK_FAILURE() ((void)0) - -/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf etc. -and check */ -#ifdef BLARGG_SOURCE_BEGIN - #include BLARGG_SOURCE_BEGIN -#endif - -#endif diff --git a/quicknes/make/.gitignore b/quicknes/make/.gitignore new file mode 100644 index 0000000000..a5edc68366 --- /dev/null +++ b/quicknes/make/.gitignore @@ -0,0 +1,2 @@ +*.dll +*.so diff --git a/quicknes/make/Makefile b/quicknes/make/Makefile index 265308a4fe..1a361b9ac0 100644 --- a/quicknes/make/Makefile +++ b/quicknes/make/Makefile @@ -13,11 +13,12 @@ endif CXXFLAGS_32 = -march=pentium4 -mtune=core2 CXXFLAGS_64 = -CXXFLAGS = -Wall -DDISABLE_AUTO_FILE -D__LIBRETRO__ -DNDEBUG -I.. -O3 -Wno-multichar -fno-exceptions -fomit-frame-pointer -flto $(CXXFLAGS_$(ARCH)) +CXXFLAGS = -Wall -I../core/source/quickerNES/core/ -I../core/extern -O3 -Wfatal-errors -Werror -fomit-frame-pointer -flto -D_QUICKERNES_DETECT_JOYPAD_READS -D_QUICKERNES_ENABLE_TRACEBACK_SUPPORT $(CXXFLAGS_$(ARCH)) + # TODO: include these as options in the Makefile # -fprofile-generate # -fprofile-use -TARGET = libquicknes.dll +TARGET = libquicknes.so LDFLAGS_32 = -static -static-libgcc -static-libstdc++ LDFLAGS_64 = LDFLAGS = -shared $(LDFLAGS_$(ARCH)) $(CXXFLAGS) @@ -26,43 +27,26 @@ DEST_64 = ../../Assets/dll DESTCOPY_64 = ../../output/dll SRCS = \ - ../nes_emu/abstract_file.cpp \ - ../nes_emu/apu_state.cpp \ - ../nes_emu/Blip_Buffer.cpp \ - ../nes_emu/Effects_Buffer.cpp \ - ../nes_emu/Mapper_Fme7.cpp \ - ../nes_emu/Mapper_Mmc5.cpp \ - ../nes_emu/Mapper_Namco106.cpp \ - ../nes_emu/Mapper_Vrc6.cpp \ - ../nes_emu/misc_mappers.cpp \ - ../nes_emu/Multi_Buffer.cpp \ - ../nes_emu/Nes_Apu.cpp \ - ../nes_emu/Nes_Buffer.cpp \ - ../nes_emu/Nes_Cart.cpp \ - ../nes_emu/Nes_Core.cpp \ - ../nes_emu/Nes_Cpu.cpp \ - ../nes_emu/nes_data.cpp \ - ../nes_emu/Nes_Effects_Buffer.cpp \ - ../nes_emu/Nes_Emu.cpp \ - ../nes_emu/Nes_File.cpp \ - ../nes_emu/Nes_Fme7_Apu.cpp \ - ../nes_emu/Nes_Mapper.cpp \ - ../nes_emu/nes_mappers.cpp \ - ../nes_emu/Nes_Mmc1.cpp \ - ../nes_emu/Nes_Mmc3.cpp \ - ../nes_emu/Nes_Namco_Apu.cpp \ - ../nes_emu/Nes_Oscs.cpp \ - ../nes_emu/Nes_Ppu.cpp \ - ../nes_emu/Nes_Ppu_Impl.cpp \ - ../nes_emu/Nes_Ppu_Rendering.cpp \ - ../nes_emu/Nes_State.cpp \ - ../nes_emu/nes_util.cpp \ - ../nes_emu/Nes_Vrc6_Apu.cpp \ - ../nes_emu/Mmc24.cpp \ - ../bizinterface.cpp \ - ../fex/Data_Reader.cpp \ - ../fex/blargg_errors.cpp \ - ../fex/blargg_common.cpp + ../core/source/quickerNES/core/apu/vrc7/emu2413_state.cpp \ + ../core/source/quickerNES/core/apu/vrc7/emu2413.cpp \ + ../core/source/quickerNES/core/apu/vrc7/apu_vrc7.cpp \ + ../core/source/quickerNES/core/apu/fme7/apu_fme7.cpp \ + ../core/source/quickerNES/core/apu/namco/apu_namco.cpp \ + ../core/source/quickerNES/core/apu/effectsBuffer.cpp \ + ../core/source/quickerNES/core/apu/blipBuffer.cpp \ + ../core/source/quickerNES/core/apu/NESEffectsBuffer.cpp \ + ../core/source/quickerNES/core/apu/vrc6/apu_vrc6.cpp \ + ../core/source/quickerNES/core/apu/oscs.cpp \ + ../core/source/quickerNES/core/apu/apu.cpp \ + ../core/source/quickerNES/core/apu/buffer.cpp \ + ../core/source/quickerNES/core/apu/multiBuffer.cpp \ + ../core/source/quickerNES/core/emu.cpp \ + ../core/source/quickerNES/core/mappers/mapper.cpp \ + ../core/source/quickerNES/core/cpu.cpp \ + ../core/source/quickerNES/core/ppu/ppu.cpp \ + ../core/source/quickerNES/core/ppu/ppuRendering.cpp \ + ../core/source/quickerNES/core/ppu/ppuImpl.cpp \ + ../bizinterface.cpp OBJS = $(SRCS:.cpp=.o) @@ -75,8 +59,8 @@ $(TARGET) : $(OBJS) $(CXX) -o $@ $(LDFLAGS) $(OBJS) clean: - $(RM) $(OBJS) - $(RM) $(TARGET) + $(RM) -f $(OBJS) + $(RM) -f $(TARGET) install: $(CP) $(TARGET) $(DEST_$(ARCH)) diff --git a/quicknes/msvc/.gitignore b/quicknes/msvc/.gitignore new file mode 100644 index 0000000000..7675c3061f --- /dev/null +++ b/quicknes/msvc/.gitignore @@ -0,0 +1,4 @@ +x64 +*.aps +*.rc +*.h diff --git a/quicknes/msvc/libquicknes.sln b/quicknes/msvc/libquicknes.sln index b2938de04d..44acfb527b 100644 --- a/quicknes/msvc/libquicknes.sln +++ b/quicknes/msvc/libquicknes.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 +VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libquicknes", "libquicknes.vcxproj", "{F07F76D3-08E6-4EBC-82F9-53FF90ABD9A9}" EndProject diff --git a/quicknes/msvc/libquicknes.vcxproj b/quicknes/msvc/libquicknes.vcxproj index cbcc7b6721..664b35fe45 100644 --- a/quicknes/msvc/libquicknes.vcxproj +++ b/quicknes/msvc/libquicknes.vcxproj @@ -11,86 +11,112 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + {F07F76D3-08E6-4EBC-82F9-53FF90ABD9A9} @@ -102,14 +128,14 @@ DynamicLibrary true MultiByte - v142 + v143 DynamicLibrary false true MultiByte - v142 + v143 @@ -121,13 +147,19 @@ + + ..\core\source\quickerNES\core;..\core\extern;$(IncludePath) + + + ..\core\source\quickerNES\core;..\core\extern;$(IncludePath) + Level3 Disabled 4244;4800;4804;4996 $(ProjectDir)\.. - _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE;__LIBRETRO__ + _WINDLL;%(PreprocessorDefinitions);_QUICKERNES_DETECT_JOYPAD_READS;_QUICKERNES_ENABLE_TRACEBACK_SUPPORT; true @@ -146,7 +178,7 @@ true 4244;4800;4804;4996 $(ProjectDir)\.. - _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE;__LIBRETRO__;NDEBUG + _WINDLL;%(PreprocessorDefinitions);_QUICKERNES_DETECT_JOYPAD_READS;_QUICKERNES_ENABLE_TRACEBACK_SUPPORT; true diff --git a/quicknes/msvc/libquicknes.vcxproj.filters b/quicknes/msvc/libquicknes.vcxproj.filters deleted file mode 100644 index f8b33fa5e1..0000000000 --- a/quicknes/msvc/libquicknes.vcxproj.filters +++ /dev/null @@ -1,267 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {ba7f113f-5234-44b7-a84c-35ad9dd39db2} - - - {704585a9-2165-422f-8457-b6c5ffe9179d} - - - {109f60b9-f5f8-4f85-807d-b0c977848674} - - - {278454e6-01bf-4140-8707-bc2a4512c2ac} - - - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\nes_emu - - - Source Files\fex - - - Source Files\fex - - - Source Files\fex - - - Source Files - - - Source Files\nes_emu - - - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\nes_emu - - - Header Files\fex - - - Header Files\fex - - - Header Files\fex - - - Header Files\fex - - - Header Files\fex - - - Header Files\fex - - - Header Files\nes_emu - - - \ No newline at end of file diff --git a/quicknes/nes_emu/Blip_Buffer.cpp b/quicknes/nes_emu/Blip_Buffer.cpp deleted file mode 100644 index e106889b04..0000000000 --- a/quicknes/nes_emu/Blip_Buffer.cpp +++ /dev/null @@ -1,412 +0,0 @@ - -// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ - -#include "Blip_Buffer.h" - -#include -#include -#include -#include -#include - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -int const buffer_extra = blip_widest_impulse_ + 2; - -Blip_Buffer::Blip_Buffer() -{ - factor_ = LONG_MAX; - offset_ = 0; - buffer_ = 0; - buffer_size_ = 0; - sample_rate_ = 0; - reader_accum = 0; - bass_shift = 0; - clock_rate_ = 0; - bass_freq_ = 16; - length_ = 0; - - // assumptions code makes about implementation-defined features - #ifndef NDEBUG - // right shift of negative value preserves sign - buf_t_ i = -0x7FFFFFFE; - assert( (i >> 1) == -0x3FFFFFFF ); - - // casting to short truncates to 16 bits and sign-extends - i = 0x18000; - assert( (short) i == -0x8000 ); - #endif -} - -Blip_Buffer::~Blip_Buffer() -{ - free( buffer_ ); -} - -void Blip_Buffer::clear( int entire_buffer ) -{ - offset_ = 0; - reader_accum = 0; - if ( buffer_ ) - { - long count = (entire_buffer ? buffer_size_ : samples_avail()); - memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) ); - } -} - -Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) -{ - // start with maximum length that resampled time can represent - long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64; - if ( msec != blip_max_length ) - { - long s = (new_rate * (msec + 1) + 999) / 1000; - if ( s < new_size ) - new_size = s; - else - assert( 0 ); // fails if requested buffer length exceeds limit - } - - if ( buffer_size_ != new_size ) - { - void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ ); - if ( !p ) - return "Out of memory"; - buffer_ = (buf_t_*) p; - } - - buffer_size_ = new_size; - - // update things based on the sample rate - sample_rate_ = new_rate; - length_ = new_size * 1000 / new_rate - 1; - if ( msec ) - assert( length_ == msec ); // ensure length is same as that passed in - if ( clock_rate_ ) - clock_rate( clock_rate_ ); - bass_freq( bass_freq_ ); - - clear(); - - return 0; // success -} - -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const -{ - double ratio = (double) sample_rate_ / clock_rate; - long factor = (long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large - return (blip_resampled_time_t) factor; -} - -void Blip_Buffer::bass_freq( int freq ) -{ - bass_freq_ = freq; - int shift = 31; - if ( freq > 0 ) - { - shift = 13; - long f = (freq << 16) / sample_rate_; - while ( (f >>= 1) && --shift ) { } - } - bass_shift = shift; -} - -void Blip_Buffer::end_frame( blip_time_t t ) -{ - offset_ += t * factor_; - assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length -} - -void Blip_Buffer::remove_silence( long count ) -{ - assert( count <= samples_avail() ); // tried to remove more samples than available - offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; -} - -long Blip_Buffer::count_samples( blip_time_t t ) const -{ - unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; - unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; - return (long) (last_sample - first_sample); -} - -blip_time_t Blip_Buffer::count_clocks( long count ) const -{ - if ( count > buffer_size_ ) - count = buffer_size_; - blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; - return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); -} - -void Blip_Buffer::remove_samples( long count ) -{ - if ( count ) - { - remove_silence( count ); - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + buffer_extra; - memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); - memset( buffer_ + remain, 0, count * sizeof *buffer_ ); - } -} - -// Blip_Synth_ - -Blip_Synth_::Blip_Synth_( short* p, int w ) : - impulses( p ), - width( w ) -{ - volume_unit_ = 0.0; - kernel_unit = 0; - buf = 0; - last_amp = 0; - delta_factor = 0; -} - -// TODO: apparently this is defined elsewhere too -#define pi my_pi -static double const pi = 3.1415926535897932384626433832795029; - -static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) -{ - if ( cutoff >= 0.999 ) - cutoff = 0.999; - - if ( treble < -300.0 ) - treble = -300.0; - if ( treble > 5.0 ) - treble = 5.0; - - double const maxh = 4096.0; - double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); - double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); - double const to_angle = pi / 2 / maxh / oversample; - for ( int i = 0; i < count; i++ ) - { - double angle = ((i - count) * 2 + 1) * to_angle; - double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); - double cos_nc_angle = cos( maxh * cutoff * angle ); - double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); - double cos_angle = cos( angle ); - - c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; - double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); - double b = 2.0 - cos_angle - cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d - } -} - -void blip_eq_t::generate( float* out, int count ) const -{ - // lower cutoff freq for narrow kernels with their wider transition band - // (8 points->1.49, 16 points->1.15) - double oversample = blip_res * 2.25 / count + 0.85; - double half_rate = sample_rate * 0.5; - if ( cutoff_freq ) - oversample = half_rate / cutoff_freq; - double cutoff = rolloff_freq * oversample / half_rate; - - gen_sinc( out, count, blip_res * oversample, treble, cutoff ); - - // apply (half of) hamming window - double to_fraction = pi / (count - 1); - for ( int i = count; i--; ) - out [i] *= 0.54 - 0.46 * cos( i * to_fraction ); -} - -void Blip_Synth_::adjust_impulse() -{ - // sum pairs for each phase and add error correction to end of first half - int const size = impulses_size(); - for ( int p = blip_res; p-- >= blip_res / 2; ) - { - int p2 = blip_res - 2 - p; - long error = kernel_unit; - for ( int i = 1; i < size; i += blip_res ) - { - error -= impulses [i + p ]; - error -= impulses [i + p2]; - } - if ( p == p2 ) - error /= 2; // phase = 0.5 impulse uses same half for both sides - impulses [size - blip_res + p] += error; - //printf( "error: %ld\n", error ); - } - - //for ( int i = blip_res; i--; printf( "\n" ) ) - // for ( int j = 0; j < width / 2; j++ ) - // printf( "%5ld,", impulses [j * blip_res + i + 1] ); -} - -void Blip_Synth_::treble_eq( blip_eq_t const& eq ) -{ - float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; - - int const half_size = blip_res / 2 * (width - 1); - eq.generate( &fimpulse [blip_res], half_size ); - - int i; - - // need mirror slightly past center for calculation - for ( i = blip_res; i--; ) - fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; - - // starts at 0 - for ( i = 0; i < blip_res; i++ ) - fimpulse [i] = 0.0f; - - // find rescale factor - double total = 0.0; - for ( i = 0; i < half_size; i++ ) - total += fimpulse [blip_res + i]; - - //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB - //double const base_unit = 37888.0; // allows treble to +5 dB - double const base_unit = 32768.0; // necessary for blip_unscaled to work - double rescale = base_unit / 2 / total; - kernel_unit = (long) base_unit; - - // integrate, first difference, rescale, convert to int - double sum = 0.0; - double next = 0.0; - int const impulses_size = this->impulses_size(); - for ( i = 0; i < impulses_size; i++ ) - { - impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); - sum += fimpulse [i]; - next += fimpulse [i + blip_res]; - } - adjust_impulse(); - - // volume might require rescaling - double vol = volume_unit_; - if ( vol ) - { - volume_unit_ = 0.0; - volume_unit( vol ); - } -} - -void Blip_Synth_::volume_unit( double new_unit ) -{ - if ( new_unit != volume_unit_ ) - { - // use default eq if it hasn't been set yet - if ( !kernel_unit ) - treble_eq( -8.0 ); - - volume_unit_ = new_unit; - double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; - - if ( factor > 0.0 ) - { - int shift = 0; - - // if unit is really small, might need to attenuate kernel - while ( factor < 2.0 ) - { - shift++; - factor *= 2.0; - } - - if ( shift ) - { - kernel_unit >>= shift; - assert( kernel_unit > 0 ); // fails if volume unit is too low - - // keep values positive to avoid round-towards-zero of sign-preserving - // right shift for negative values - long offset = 0x8000 + (1 << (shift - 1)); - long offset2 = 0x8000 >> shift; - for ( int i = impulses_size(); i--; ) - impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); - adjust_impulse(); - } - } - delta_factor = (int) floor( factor + 0.5 ); - //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); - } -} - -long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo ) -{ - long count = samples_avail(); - if ( count > max_samples ) - count = max_samples; - - if ( count ) - { - int const sample_shift = blip_sample_bits - 16; - int const bass_shift = this->bass_shift; - long accum = reader_accum; - buf_t_* in = buffer_; - - if ( !stereo ) - { - for ( long n = count; n--; ) - { - long s = accum >> sample_shift; - accum -= accum >> bass_shift; - accum += *in++; - *out++ = (blip_sample_t) s; - - // clamp sample - if ( (blip_sample_t) s != s ) - out [-1] = (blip_sample_t) (0x7FFF - (s >> 24)); - } - } - else - { - for ( long n = count; n--; ) - { - long s = accum >> sample_shift; - accum -= accum >> bass_shift; - accum += *in++; - *out = (blip_sample_t) s; - out += 2; - - // clamp sample - if ( (blip_sample_t) s != s ) - out [-2] = (blip_sample_t) (0x7FFF - (s >> 24)); - } - } - - reader_accum = accum; - remove_samples( count ); - } - return count; -} - -void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) -{ - buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; - - int const sample_shift = blip_sample_bits - 16; - int prev = 0; - while ( count-- ) - { - long s = (long) *in++ << sample_shift; - *out += s - prev; - prev = s; - ++out; - } - *out -= prev; -} - diff --git a/quicknes/nes_emu/Blip_Buffer.h b/quicknes/nes_emu/Blip_Buffer.h deleted file mode 100644 index 49a156a3f6..0000000000 --- a/quicknes/nes_emu/Blip_Buffer.h +++ /dev/null @@ -1,354 +0,0 @@ - -// Band-limited sound synthesis and buffering - -// Blip_Buffer 0.4.0 - -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - -// Time unit at source clock rate -typedef long blip_time_t; - -// Output samples are 16-bit signed, with a range of -32768 to 32767 -typedef short blip_sample_t; -enum { blip_sample_max = 32767 }; - -class Blip_Buffer { -public: - typedef const char* blargg_err_t; - - // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults - // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there - // isn't enough memory, returns error without affecting current buffer setup. - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); - - // Set number of source time units per second - void clock_rate( long ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begins - // a new time frame at the end of the current frame. - void end_frame( blip_time_t time ); - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Returns number of samples actually read and removed. If stereo is - // true, increments 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); - -// Additional optional features - - // Current output sample rate - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // Number of source time units per second - long clock_rate() const; - - // Set frequency high-pass filter frequency, where higher values reduce bass more - void bass_freq( int frequency ); - - // Number of samples delay from synthesis to samples read out - int output_latency() const; - - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clears out any samples waiting rather than the entire buffer. - void clear( int entire_buffer = 1 ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - -// Experimental features - - // Number of raw samples that can be mixed within frame of specified duration. - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( blip_sample_t const* buf, long count ); - - // Count number of clocks needed until 'count' samples will be available. - // If buffer can't even hold 'count' samples, returns number of clocks until - // buffer becomes full. - blip_time_t count_clocks( long count ) const; - - // not documented yet - typedef unsigned long blip_resampled_time_t; - void remove_silence( long count ); - blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } - blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } - blip_resampled_time_t clock_rate_factor( long clock_rate ) const; -public: - Blip_Buffer(); - ~Blip_Buffer(); - - // Deprecated - typedef blip_resampled_time_t resampled_time_t; - blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } - blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } -private: - // noncopyable - Blip_Buffer( const Blip_Buffer& ); - Blip_Buffer& operator = ( const Blip_Buffer& ); -public: - typedef long buf_t_; - unsigned long factor_; - blip_resampled_time_t offset_; - buf_t_* buffer_; - long buffer_size_; -private: - long reader_accum; - int bass_shift; - long sample_rate_; - long clock_rate_; - int bass_freq_; - int length_; - friend class Blip_Reader; -}; - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -// Number of bits in resample ratio fraction. Higher values give a more accurate ratio -// but reduce maximum buffer size. -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif - -// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in -// noticeable broadband noise when synthesizing high frequency square waves. -// Affects size of Blip_Synth objects since they store the waveform directly. -#ifndef BLIP_PHASE_BITS - #define BLIP_PHASE_BITS 6 -#endif - - // Internal - typedef unsigned long blip_resampled_time_t; - int const blip_widest_impulse_ = 16; - int const blip_res = 1 << BLIP_PHASE_BITS; - class blip_eq_t; - - class Blip_Synth_ { - double volume_unit_; - short* const impulses; - int const width; - long kernel_unit; - int impulses_size() const { return blip_res / 2 * width + 1; } - void adjust_impulse(); - public: - Blip_Buffer* buf; - int last_amp; - int delta_factor; - - Blip_Synth_( short* impulses, int width ); - void treble_eq( blip_eq_t const& ); - void volume_unit( double ); - }; - -// Quality level. Start with blip_good_quality. -const int blip_med_quality = 8; -const int blip_good_quality = 12; -const int blip_high_quality = 16; - -// Range specifies the greatest expected change in amplitude. Calculate it -// by finding the difference between the maximum and minimum expected -// amplitudes (max - min). -template -class Blip_Synth { -public: - // Set overall volume of waveform - void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } - - // Configure low-pass filter (see notes.txt) - void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } - - // Get/set Blip_Buffer used for output - Blip_Buffer* output() const { return impl.buf; } - void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } - - // Update amplitude of waveform at given time. Using this requires a separate - // Blip_Synth for each waveform. - void update( blip_time_t time, int amplitude ); - -// Low-level interface - - // Add an amplitude transition of specified delta, optionally into specified buffer - // rather than the one set with output(). Delta can be positive or negative. - // The actual change in amplitude is delta * (volume / range) - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } - - // Works directly in terms of fractional output samples. Contact author for more. - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - - // Same as offset(), except code is inlined for higher performance - void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t t, int delta ) const { - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); - } - -public: - Blip_Synth() : impl( impulses, quality ) { } -private: - typedef short imp_t; - imp_t impulses [blip_res * (quality / 2) + 1]; - Blip_Synth_ impl; -}; - -// Low-pass equalization parameters -class blip_eq_t { -public: - // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce - // treble, small positive values (0 to 5.0) increase treble. - blip_eq_t( double treble_db = 0 ); - - // See notes.txt - blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); - -private: - double treble; - long rolloff_freq; - long sample_rate; - long cutoff_freq; - void generate( float* out, int count ) const; - friend class Blip_Synth_; -}; - -int const blip_sample_bits = 30; - -// Optimized inline sample reader for custom sample formats and mixing of Blip_Buffer samples -class Blip_Reader { -public: - // Begin reading samples from buffer. Returns value to pass to next() (can - // be ignored if default bass_freq is acceptable). - int begin( Blip_Buffer& ); - - // Current sample - long read() const { return accum >> (blip_sample_bits - 16); } - - // Current raw sample in full internal resolution - long read_raw() const { return accum; } - - // Advance to next sample - void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } - - // End reading samples from buffer. The number of samples read must now be removed - // using Blip_Buffer::remove_samples(). - void end( Blip_Buffer& b ) { b.reader_accum = accum; } - -private: - const Blip_Buffer::buf_t_* buf; - long accum; -}; - - -// End of public interface - - -#include - -// Compatibility with older version -const long blip_unscaled = 65535; -const int blip_low_quality = blip_med_quality; -const int blip_best_quality = blip_high_quality; - -#define BLIP_FWD( i ) { \ - long t0 = i0 * delta + buf [fwd + i]; \ - long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i]; \ - i0 = imp [blip_res * (i + 2)]; \ - buf [fwd + i] = t0; \ - buf [fwd + 1 + i] = t1; } - -#define BLIP_REV( r ) { \ - long t0 = i0 * delta + buf [rev - r]; \ - long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r]; \ - i0 = imp [blip_res * (r - 1)]; \ - buf [rev - r] = t0; \ - buf [rev + 1 - r] = t1; } - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the - // need for a longer buffer as set by set_sample_rate(). - assert( (long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); - delta *= impl.delta_factor; - int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); - imp_t const* imp = impulses + blip_res - phase; - long* buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); - long i0 = *imp; - - int const fwd = (blip_widest_impulse_ - quality) / 2; - int const rev = fwd + quality - 2; - - BLIP_FWD( 0 ) - if ( quality > 8 ) BLIP_FWD( 2 ) - if ( quality > 12 ) BLIP_FWD( 4 ) - { - int const mid = quality / 2 - 1; - long t0 = i0 * delta + buf [fwd + mid - 1]; - long t1 = imp [blip_res * mid] * delta + buf [fwd + mid]; - imp = impulses + phase; - i0 = imp [blip_res * mid]; - buf [fwd + mid - 1] = t0; - buf [fwd + mid] = t1; - } - if ( quality > 12 ) BLIP_REV( 6 ) - if ( quality > 8 ) BLIP_REV( 4 ) - BLIP_REV( 2 ) - - long t0 = i0 * delta + buf [rev]; - long t1 = *imp * delta + buf [rev + 1]; - buf [rev] = t0; - buf [rev + 1] = t1; -} - -#undef BLIP_FWD -#undef BLIP_REV - -template -void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const -{ - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); -} - -template -void Blip_Synth::update( blip_time_t t, int amp ) -{ - int delta = amp - impl.last_amp; - impl.last_amp = amp; - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); -} - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } -inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : - treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } - -inline int Blip_Buffer::length() const { return length_; } -inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } -inline long Blip_Buffer::sample_rate() const { return sample_rate_; } -inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } -inline long Blip_Buffer::clock_rate() const { return clock_rate_; } -inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } - -inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) -{ - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum; - return blip_buf.bass_shift; -} - -int const blip_max_length = 0; -int const blip_default_length = 250; - -#endif - diff --git a/quicknes/nes_emu/Blip_Synth.h b/quicknes/nes_emu/Blip_Synth.h deleted file mode 100644 index f2eb077b88..0000000000 --- a/quicknes/nes_emu/Blip_Synth.h +++ /dev/null @@ -1,208 +0,0 @@ - -// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding -// waveforms to a Blip_Buffer. - -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_SYNTH_H -#define BLIP_SYNTH_H - -#ifndef BLIP_BUFFER_H - #include "Blip_Buffer.h" -#endif - -// Quality level. Higher levels are slower, and worse in a few cases. -// Use blip_good_quality as a starting point. -const int blip_low_quality = 1; -const int blip_med_quality = 2; -const int blip_good_quality = 3; -const int blip_high_quality = 4; - -// Blip_Synth is a transition waveform synthesizer which adds band-limited -// offsets (transitions) into a Blip_Buffer. For a simpler interface, use -// Blip_Wave (below). -// -// Range specifies the greatest expected offset that will occur. For a -// waveform that goes between +amp and -amp, range should be amp * 2 (half -// that if it only goes between +amp and 0). When range is large, a higher -// accuracy scheme is used; to force this even when range is small, pass -// the negative of range (i.e. -range). -template -class Blip_Synth { - BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); - BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 ); - enum { - abs_range = (range < 0) ? -range : range, - fine_mode = (range > 512 || range < 0), - width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_), - res = 1 << blip_res_bits_, - impulse_size = width / 2 * (fine_mode + 1), - base_impulses_size = width / 2 * (res / 2 + 1), - fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : - abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : - abs_range <= 2048 ? 7 : 8) : 0) - }; - blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; - Blip_Impulse_ impulse; - void init() { impulse.init( impulses, width, res, fine_bits ); } -public: - Blip_Synth() { init(); } - Blip_Synth( double volume ) { init(); this->volume( volume ); } - - // Configure low-pass filter (see notes.txt). Not optimized for real-time control - void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } - - // Set volume of a transition at amplitude 'range' by setting volume_unit - // to v / range - void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } - - // Set base volume unit of transitions, where 1.0 is a full swing between the - // positive and negative extremes. Not optimized for real-time control. - void volume_unit( double unit ) { impulse.volume_unit( unit ); } - - // Default Blip_Buffer used for output when none is specified for a given call - Blip_Buffer* output() const { return impulse.buf; } - void output( Blip_Buffer* b ) { impulse.buf = b; } - - // Add an amplitude offset (transition) with a magnitude of delta * volume_unit - // into the specified buffer (default buffer if none specified) at the - // specified source time. Delta can be positive or negative. To increase - // performance by inlining code at the call site, use offset_inline(). - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - void offset_resampled( blip_resampled_time_t t, int o ) const { - offset_resampled( t, o, impulse.buf ); - } - void offset( blip_time_t t, int delta ) const { - offset( t, delta, impulse.buf ); - } - void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t time, int delta ) const { - offset_inline( time, delta, impulse.buf ); - } -}; - -// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. -// A wave is built from a series of delays and new amplitudes. This provides a -// simpler interface than Blip_Synth, nothing more. -template -class Blip_Wave { - Blip_Synth synth; - blip_time_t time_; - int last_amp; - void init() { time_ = 0; last_amp = 0; } -public: - // Start wave at time 0 and amplitude 0 - Blip_Wave() { init(); } - Blip_Wave( double volume ) { init(); this->volume( volume ); } - - // See Blip_Synth for description - void volume( double v ) { synth.volume( v ); } - void volume_unit( double v ) { synth.volume_unit( v ); } - void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); } - Blip_Buffer* output() const { return synth.output(); } - void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; } - - // Current time in frame - blip_time_t time() const { return time_; } - void time( blip_time_t t ) { time_ = t; } - - // Current amplitude of wave - int amplitude() const { return last_amp; } - void amplitude( int ); - - // Move forward by 't' time units - void delay( blip_time_t t ) { time_ += t; } - - // End time frame of specified duration. Localize time to new frame. - // If wave hadn't been run to end of frame, start it at beginning of new frame. - void end_frame( blip_time_t duration ) - { - time_ -= duration; - if ( time_ < 0 ) - time_ = 0; - } -}; - -// End of public interface - -template -void Blip_Wave::amplitude( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - synth.offset_inline( time_, delta ); -} - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - typedef blip_pair_t_ pair_t; - - unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1; - assert(( "Blip_Synth/Blip_wave: Went past end of buffer", - sample_index < blip_buf->buffer_size_ )); - enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; - pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; - - enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; - enum { mask = res * 2 - 1 }; - const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; - - pair_t offset = impulse.offset * delta; - - if ( !fine_bits ) - { - // normal mode - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta; - t1 += imp [1] * delta; - imp += 2; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } - else - { - // fine mode - enum { sub_range = 1 << fine_bits }; - delta += sub_range / 2; - int delta2 = (delta & (sub_range - 1)) - sub_range / 2; - delta >>= fine_bits; - - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta2; - t0 += imp [1] * delta; - - t1 += imp [2] * delta2; - t1 += imp [3] * delta; - - imp += 4; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } -} - -template -void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); -} - -#endif - diff --git a/quicknes/nes_emu/Effects_Buffer.cpp b/quicknes/nes_emu/Effects_Buffer.cpp deleted file mode 100644 index 2ce9be0b47..0000000000 --- a/quicknes/nes_emu/Effects_Buffer.cpp +++ /dev/null @@ -1,518 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Effects_Buffer.h" - -#include - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -typedef long fixed_t; - -#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) -#define FMUL( x, y ) (((x) * (y)) >> 15) - -const unsigned echo_size = 4096; -const unsigned echo_mask = echo_size - 1; -BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2 - -const unsigned reverb_size = 8192 * 2; -const unsigned reverb_mask = reverb_size - 1; -BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2 - -Effects_Buffer::config_t::config_t() -{ - pan_1 = -0.15f; - pan_2 = 0.15f; - reverb_delay = 88.0f; - reverb_level = 0.12f; - echo_delay = 61.0f; - echo_level = 0.10f; - delay_variance = 18.0f; - effects_enabled = false; -} - -void Effects_Buffer::set_depth( double d ) -{ - float f = (float) d; - config_t c; - c.pan_1 = -0.6f * f; - c.pan_2 = 0.6f * f; - c.reverb_delay = 880 * 0.1f; - c.echo_delay = 610 * 0.1f; - if ( f > 0.5 ) - f = 0.5; // TODO: more linear reduction of extreme reverb/echo - c.reverb_level = 0.5f * f; - c.echo_level = 0.30f * f; - c.delay_variance = 180 * 0.1f; - c.effects_enabled = (d > 0.0f); - config( c ); -} - -Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 ) -{ - buf_count = center_only ? max_buf_count - 4 : max_buf_count; - - echo_buf = NULL; - echo_pos = 0; - - reverb_buf = NULL; - reverb_pos = 0; - - stereo_remain = 0; - effect_remain = 0; - effects_enabled = false; - set_depth( 0 ); -} - -Effects_Buffer::~Effects_Buffer() -{ - delete [] echo_buf; - delete [] reverb_buf; -} - -blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) -{ - if ( !echo_buf ) - { - echo_buf = BLARGG_NEW blip_sample_t [echo_size]; - CHECK_ALLOC( echo_buf ); - } - - if ( !reverb_buf ) - { - reverb_buf = BLARGG_NEW blip_sample_t [reverb_size]; - CHECK_ALLOC( reverb_buf ); - } - - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - - config( config_ ); - clear(); - - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Effects_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Effects_Buffer::bass_freq( int freq ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( freq ); -} - -void Effects_Buffer::clear() -{ - stereo_remain = 0; - effect_remain = 0; - if ( echo_buf ) - memset( echo_buf, 0, echo_size * sizeof *echo_buf ); - if ( reverb_buf ) - memset( reverb_buf, 0, reverb_size * sizeof *reverb_buf ); - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -inline int pin_range( int n, int max, int min = 0 ) -{ - if ( n < min ) - return min; - if ( n > max ) - return max; - return n; -} - -void Effects_Buffer::config( const config_t& cfg ) -{ - channels_changed(); - - // clear echo and reverb buffers - if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf ) - { - memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) ); - memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) ); - } - - config_ = cfg; - - if ( config_.effects_enabled ) - { - // convert to internal format - - chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); - chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0]; - - chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 ); - chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; - - chans.reverb_level = TO_FIXED( config_.reverb_level ); - chans.echo_level = TO_FIXED( config_.echo_level ); - - int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); - - int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate()); - chans.reverb_delay_l = pin_range( reverb_size - - (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); - chans.reverb_delay_r = pin_range( reverb_size + 1 - - (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); - - int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); - chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset), - echo_size - 1 ); - chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), - echo_size - 1 ); - - // set up outputs - for ( unsigned i = 0; i < chan_count; i++ ) - { - channel_t& o = channels [i]; - if ( i < 2 ) - { - o.center = &bufs [i]; - o.left = &bufs [3]; - o.right = &bufs [4]; - } - else - { - o.center = &bufs [2]; - o.left = &bufs [5]; - o.right = &bufs [6]; - } - } - - } - else - { - // set up outputs - for ( unsigned i = 0; i < chan_count; i++ ) - { - channel_t& o = channels [i]; - o.center = &bufs [0]; - o.left = &bufs [1]; - o.right = &bufs [2]; - } - } - - if ( buf_count < max_buf_count ) - { - for ( unsigned i = 0; i < chan_count; i++ ) - { - channel_t& o = channels [i]; - o.left = o.center; - o.right = o.center; - } - } -} - -void Effects_Buffer::end_frame( blip_time_t clock_count, bool stereo ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].end_frame( clock_count ); - - if ( stereo && buf_count == max_buf_count ) - stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - if ( effects_enabled || config_.effects_enabled ) - effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - effects_enabled = config_.effects_enabled; -} - -long Effects_Buffer::samples_avail() const -{ - return bufs [0].samples_avail() * 2; -} - -long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) -{ - require( total_samples % 2 == 0 ); // count must be even - - long remain = bufs [0].samples_avail(); - if ( remain > (total_samples >> 1) ) - remain = (total_samples >> 1); - total_samples = remain; - while ( remain ) - { - int active_bufs = buf_count; - long count = remain; - - // optimizing mixing to skip any channels which had nothing added - if ( effect_remain ) - { - if ( count > effect_remain ) - count = effect_remain; - - if ( stereo_remain ) - { - mix_enhanced( out, count ); - } - else - { - mix_mono_enhanced( out, count ); - active_bufs = 3; - } - } - else if ( stereo_remain ) - { - mix_stereo( out, count ); - active_bufs = 3; - } - else - { - mix_mono( out, count ); - active_bufs = 1; - } - - out += count * 2; - remain -= count; - - stereo_remain -= count; - if ( stereo_remain < 0 ) - stereo_remain = 0; - - effect_remain -= count; - if ( effect_remain < 0 ) - effect_remain = 0; - - for ( int i = 0; i < buf_count; i++ ) - { - if ( i < active_bufs ) - bufs [i].remove_samples( count ); - else - bufs [i].remove_silence( count ); // keep time synchronized - } - } - - return total_samples * 2; -} - -void Effects_Buffer::mix_mono( blip_sample_t* out, long count ) -{ - Blip_Reader c; - int shift = c.begin( bufs [0] ); - - // unrolled loop - for ( long n = count >> 1; n--; ) - { - long cs0 = c.read(); - c.next( shift ); - - long cs1 = c.read(); - c.next( shift ); - - if ( (BOOST::int16_t) cs0 != cs0 ) - cs0 = 0x7FFF - (cs0 >> 24); - ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); - - if ( (BOOST::int16_t) cs1 != cs1 ) - cs1 = 0x7FFF - (cs1 >> 24); - ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); - out += 4; - } - - if ( count & 1 ) - { - int s = c.read(); - c.next( shift ); - out [0] = s; - out [1] = s; - if ( (BOOST::int16_t) s != s ) - { - s = 0x7FFF - (s >> 24); - out [0] = s; - out [1] = s; - } - } - - c.end( bufs [0] ); -} - -void Effects_Buffer::mix_stereo( blip_sample_t* out, long count ) -{ - Blip_Reader l; l.begin( bufs [1] ); - Blip_Reader r; r.begin( bufs [2] ); - Blip_Reader c; - int shift = c.begin( bufs [0] ); - - while ( count-- ) - { - int cs = c.read(); - c.next( shift ); - int left = cs + l.read(); - int right = cs + r.read(); - l.next( shift ); - r.next( shift ); - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - - c.end( bufs [0] ); - r.end( bufs [2] ); - l.end( bufs [1] ); -} - -void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out, long count ) -{ - Blip_Reader sq1; sq1.begin( bufs [0] ); - Blip_Reader sq2; sq2.begin( bufs [1] ); - Blip_Reader center; - int shift = center.begin( bufs [2] ); - - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - while ( count-- ) - { - int sum1_s = sq1.read(); - int sum2_s = sq2.read(); - - sq1.next( shift ); - sq2.next( shift ); - - int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + - FMUL( sum2_s, chans.pan_2_levels [0] ) + - reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - - int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + - FMUL( sum2_s, chans.pan_2_levels [1] ) + - reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - - fixed_t reverb_level = chans.reverb_level; - reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level ); - reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level ); - reverb_pos = (reverb_pos + 2) & reverb_mask; - - int sum3_s = center.read(); - center.next( shift ); - - int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); - int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - - echo_buf [echo_pos] = sum3_s; - echo_pos = (echo_pos + 1) & echo_mask; - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - sq1.end( bufs [0] ); - sq2.end( bufs [1] ); - center.end( bufs [2] ); -} - -void Effects_Buffer::mix_enhanced( blip_sample_t* out, long count ) -{ - Blip_Reader l1; l1.begin( bufs [3] ); - Blip_Reader r1; r1.begin( bufs [4] ); - Blip_Reader l2; l2.begin( bufs [5] ); - Blip_Reader r2; r2.begin( bufs [6] ); - Blip_Reader sq1; sq1.begin( bufs [0] ); - Blip_Reader sq2; sq2.begin( bufs [1] ); - Blip_Reader center; - int shift = center.begin( bufs [2] ); - - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - while ( count-- ) - { - int sum1_s = sq1.read(); - int sum2_s = sq2.read(); - - sq1.next( shift ); - sq2.next( shift ); - - int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + - FMUL( sum2_s, chans.pan_2_levels [0] ) + l1.read() + - reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - - int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + - FMUL( sum2_s, chans.pan_2_levels [1] ) + r1.read() + - reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - - l1.next( shift ); - r1.next( shift ); - - fixed_t reverb_level = chans.reverb_level; - reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level ); - reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level ); - reverb_pos = (reverb_pos + 2) & reverb_mask; - - int sum3_s = center.read(); - center.next( shift ); - - int left = new_reverb_l + sum3_s + l2.read() + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); - int right = new_reverb_r + sum3_s + r2.read() + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - - l2.next( shift ); - r2.next( shift ); - - echo_buf [echo_pos] = sum3_s; - echo_pos = (echo_pos + 1) & echo_mask; - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - sq1.end( bufs [0] ); - sq2.end( bufs [1] ); - center.end( bufs [2] ); - l1.end( bufs [3] ); - r1.end( bufs [4] ); - l2.end( bufs [5] ); - r2.end( bufs [6] ); -} - diff --git a/quicknes/nes_emu/Effects_Buffer.h b/quicknes/nes_emu/Effects_Buffer.h deleted file mode 100644 index d16085c8a6..0000000000 --- a/quicknes/nes_emu/Effects_Buffer.h +++ /dev/null @@ -1,93 +0,0 @@ - -// Multi-channel effects buffer with panning, echo and reverb - -// Game_Music_Emu 0.3.0 - -#ifndef EFFECTS_BUFFER_H -#define EFFECTS_BUFFER_H - -#include "Multi_Buffer.h" - -// Effects_Buffer uses several buffers and outputs stereo sample pairs. -class Effects_Buffer : public Multi_Buffer { -public: - // If center_only is true, only center buffers are created and - // less memory is used. - Effects_Buffer( bool center_only = false ); - - // Channel Effect Center Pan - // --------------------------------- - // 0,5 reverb pan_1 - // 1,6 reverb pan_2 - // 2,7 echo - - // 3 echo - - // 4 echo - - - // Channel configuration - struct config_t { - double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right - double pan_2; - double echo_delay; // msec - double echo_level; // 0.0 to 1.0 - double reverb_delay; // msec - double delay_variance; // difference between left/right delays (msec) - double reverb_level; // 0.0 to 1.0 - bool effects_enabled; // if false, use optimized simple mixer - config_t(); - }; - - // Set configuration of buffer - virtual void config( const config_t& ); - void set_depth( double ); - -public: - ~Effects_Buffer(); - blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool was_stereo = true ); - long read_samples( blip_sample_t*, long ); - long samples_avail() const; -private: - typedef long fixed_t; - - enum { max_buf_count = 7 }; - Blip_Buffer bufs [max_buf_count]; - enum { chan_count = 5 }; - channel_t channels [chan_count]; - config_t config_; - long stereo_remain; - long effect_remain; - int buf_count; - bool effects_enabled; - - blip_sample_t* reverb_buf; - blip_sample_t* echo_buf; - int reverb_pos; - int echo_pos; - - struct { - fixed_t pan_1_levels [2]; - fixed_t pan_2_levels [2]; - int echo_delay_l; - int echo_delay_r; - fixed_t echo_level; - int reverb_delay_l; - int reverb_delay_r; - fixed_t reverb_level; - } chans; - - void mix_mono( blip_sample_t*, long ); - void mix_stereo( blip_sample_t*, long ); - void mix_enhanced( blip_sample_t*, long ); - void mix_mono_enhanced( blip_sample_t*, long ); -}; - - inline Effects_Buffer::channel_t Effects_Buffer::channel( int i ) { - return channels [i % chan_count]; - } - -#endif - diff --git a/quicknes/nes_emu/Mapper_Fme7.cpp b/quicknes/nes_emu/Mapper_Fme7.cpp deleted file mode 100644 index 97a08983a6..0000000000 --- a/quicknes/nes_emu/Mapper_Fme7.cpp +++ /dev/null @@ -1,213 +0,0 @@ - -// Sunsoft FME-7 mapper - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/libs/ - -#include "Nes_Mapper.h" - -#include "blargg_endian.h" -#include "Nes_Fme7_Apu.h" - -/* Copyright (C) 2005 Chris Moeller */ -/* Copyright (C) 2005-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -struct fme7_state_t -{ - // first 16 bytes in register order - BOOST::uint8_t regs [13]; - BOOST::uint8_t irq_mode; - BOOST::uint16_t irq_count; - - BOOST::uint8_t command; - BOOST::uint8_t irq_pending; - fme7_apu_state_t sound_state; // only used when saving/restoring state - - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (fme7_state_t) == 18 + sizeof (fme7_apu_state_t) ); - -void fme7_state_t::swap() -{ - set_le16( &irq_count, irq_count ); - for ( unsigned i = 0; i < sizeof sound_state.delays / sizeof sound_state.delays [0]; i++ ) - set_le16( &sound_state.delays [i], sound_state.delays [i] ); -} - -class Mapper_Fme7 : public Nes_Mapper, fme7_state_t { - nes_time_t last_time; - Nes_Fme7_Apu sound; -public: - Mapper_Fme7() - { - fme7_state_t* state = this; - register_state( state, sizeof *state ); - } - - virtual int channel_count() const { return sound.osc_count; } - - virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); } - - virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); } - - virtual void reset_state() - { - regs [8] = 0x40; // wram disabled - irq_count = 0xFFFF; - sound.reset(); - } - - virtual void save_state( mapper_state_t& out ) - { - sound.save_state( &sound_state ); - fme7_state_t::swap(); - Nes_Mapper::save_state( out ); - fme7_state_t::swap(); // to do: kind of hacky to swap in place - } - - virtual void read_state( mapper_state_t const& in ) - { - Nes_Mapper::read_state( in ); - fme7_state_t::swap(); - sound.load_state( sound_state ); - } - - void write_register( int index, int data ); - - virtual void apply_mapping() - { - last_time = 0; - for ( int i = 0; i < (int) sizeof regs; i++ ) - write_register( i, regs [i] ); - } - - virtual void run_until( nes_time_t end_time ) - { - int new_count = irq_count - (end_time - last_time); - last_time = end_time; - - if ( new_count <= 0 && (irq_mode & 0x81) == 0x81 ) - irq_pending = true; - - if ( irq_mode & 0x01 ) - irq_count = new_count & 0xFFFF; - } - - virtual nes_time_t next_irq( nes_time_t ) - { - if ( irq_pending ) - return 0; - - if ( (irq_mode & 0x81) == 0x81 ) - return last_time + irq_count + 1; - - return no_irq; - } - - virtual void end_frame( nes_time_t end_time ) - { - if ( end_time > last_time ) - run_until( end_time ); - - last_time -= end_time; - assert( last_time >= 0 ); - - sound.end_frame( end_time ); - } - - void write_irq( nes_time_t, int index, int data ); - - virtual void write( nes_time_t time, nes_addr_t addr, int data ) - { - switch ( addr & 0xE000 ) - { - case 0x8000: - command = data & 0x0F; - break; - - case 0xA000: - if ( command < 0x0D ) - write_register( command, data ); - else - write_irq( time, command, data ); - break; - - case 0xC000: - sound.write_latch( data ); - break; - - case 0xE000: - sound.write_data( time, data ); - break; - } - } -}; - -void Mapper_Fme7::write_irq( nes_time_t time, int index, int data ) -{ - run_until( time ); - switch ( index ) - { - case 0x0D: - irq_mode = data; - irq_pending = false; - irq_changed(); - break; - - case 0x0E: - irq_count = (irq_count & 0xFF00) | data; - break; - - case 0x0F: - irq_count = data << 8 | (irq_count & 0xFF); - break; - } - -} - -void Mapper_Fme7::write_register( int index, int data ) -{ - regs [index] = data; - int prg_bank = index - 0x09; - if ( (unsigned) prg_bank < 3 ) // most common - { - set_prg_bank( 0x8000 | (prg_bank << bank_8k), bank_8k, data ); - } - else if ( index == 0x08 ) - { - enable_sram( (data & 0xC0) == 0xC0 ); - if ( !(data & 0xC0) ) - set_prg_bank( 0x6000, bank_8k, data & 0x3F ); - } - else if ( index < 0x08 ) - { - set_chr_bank( index * 0x400, bank_1k, data ); - } - else - { - assert( index == 0x0C ); - if ( data & 2 ) - mirror_single( data & 1 ); - else if ( data & 1 ) - mirror_horiz(); - else - mirror_vert(); - } -} - -void register_fme7_mapper(); -void register_fme7_mapper() -{ - register_mapper( 69 ); -} - diff --git a/quicknes/nes_emu/Mapper_Mmc5.cpp b/quicknes/nes_emu/Mapper_Mmc5.cpp deleted file mode 100644 index ca610aeca1..0000000000 --- a/quicknes/nes_emu/Mapper_Mmc5.cpp +++ /dev/null @@ -1,155 +0,0 @@ - -// NES MMC5 mapper, currently only tailored for Castlevania 3 (U) - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -#include "Nes_Core.h" -#include - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -struct mmc5_state_t -{ - enum { reg_count = 0x30 }; - byte regs [0x30]; - byte irq_enabled; -}; -// to do: finalize state format -BOOST_STATIC_ASSERT( sizeof (mmc5_state_t) == 0x31 ); - -class Mapper_Mmc5 : public Nes_Mapper, mmc5_state_t { - nes_time_t irq_time; -public: - Mapper_Mmc5() - { - mmc5_state_t* state = this; - register_state( state, sizeof *state ); - } - - virtual void reset_state() - { - irq_time = no_irq; - regs [0x00] = 2; - regs [0x01] = 3; - regs [0x14] = 0x7f; - regs [0x15] = 0x7f; - regs [0x16] = 0x7f; - regs [0x17] = 0x7f; - } - - virtual void read_state( mapper_state_t const& in ) - { - Nes_Mapper::read_state( in ); - irq_time = no_irq; - } - - enum { regs_addr = 0x5100 }; - - virtual void apply_mapping(); - - virtual nes_time_t next_irq( nes_time_t ) - { - if ( irq_enabled & 0x80 ) - return irq_time; - - return no_irq; - } - - virtual bool write_intercepted( nes_time_t time, nes_addr_t addr, int data ) - { - int reg = addr - regs_addr; - if ( (unsigned) reg < reg_count ) - { - regs [reg] = data; - switch ( reg ) - { - case 0x05: - mirror_manual( data & 3, data >> 2 & 3, - data >> 4 & 3, data >> 6 & 3 ); - break; - - case 0x15: - set_prg_bank( 0x8000, bank_16k, data >> 1 & 0x3f ); - break; - - case 0x16: - set_prg_bank( 0xC000, bank_8k, data & 0x7f ); - break; - - case 0x17: - set_prg_bank( 0xE000, bank_8k, data & 0x7f ); - break; - - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - set_chr_bank( ((reg >> 1 & 4) + (reg & 3)) * 0x400, bank_1k, data ); - break; - } - check( (regs [0x00] & 3) == 2 ); - check( (regs [0x01] & 3) == 3 ); - } - else if ( addr == 0x5203 ) - { - irq_time = no_irq; - if ( data && data < 240 ) - { - irq_time = (341 * 21 + 128 + (data * 341)) / 3; - if ( irq_time < time ) - irq_time = no_irq; - } - irq_changed(); - } - else if ( addr == 0x5204 ) - { - irq_enabled = data; - irq_changed(); - } - else - { - return false; - } - - return true; - } - - virtual void write( nes_time_t, nes_addr_t, int ) { } -}; - -void Mapper_Mmc5::apply_mapping() -{ - static unsigned char list [] = { - 0x05, 0x15, 0x16, 0x17, - 0x20, 0x21, 0x22, 0x23, - 0x28, 0x29, 0x2a, 0x2b - }; - - for ( int i = 0; i < (int) sizeof list; i++ ) - write_intercepted( 0, regs_addr + list [i], regs [list [i]] ); - intercept_writes( 0x5100, 0x200 ); -} - -void register_mmc5_mapper(); -void register_mmc5_mapper() -{ - register_mapper( 5 ); -} - diff --git a/quicknes/nes_emu/Mapper_Namco106.cpp b/quicknes/nes_emu/Mapper_Namco106.cpp deleted file mode 100644 index 0f28802c72..0000000000 --- a/quicknes/nes_emu/Mapper_Namco106.cpp +++ /dev/null @@ -1,216 +0,0 @@ - -// Namco 106 mapper - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -#include "Nes_Namco_Apu.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -// to do: CHR mapping and nametable handling needs work - -struct namco106_state_t -{ - BOOST::uint8_t regs [16]; - BOOST::uint16_t irq_ctr; - BOOST::uint8_t irq_pending; - BOOST::uint8_t unused1 [1]; -}; -BOOST_STATIC_ASSERT( sizeof (namco106_state_t) == 20 ); - -class Mapper_Namco106 : public Nes_Mapper, namco106_state_t { - Nes_Namco_Apu sound; - nes_time_t last_time; -public: - Mapper_Namco106() - { - namco106_state_t* state = this; - register_state( state, sizeof *state ); - } - - virtual int channel_count() const { return sound.osc_count; } - - virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); } - - virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); } - - void reset_state() - { - regs [12] = 0; - regs [13] = 1; - regs [14] = last_bank - 1; - sound.reset(); - } - - virtual void apply_mapping() - { - last_time = 0; - enable_sram(); - intercept_writes( 0x4800, 1 ); - intercept_reads ( 0x4800, 1 ); - - intercept_writes( 0x5000, 0x1000 ); - intercept_reads ( 0x5000, 0x1000 ); - - for ( int i = 0; i < (int) sizeof regs; i++ ) - write( 0, 0x8000 + i * 0x800, regs [i] ); - } - - virtual nes_time_t next_irq( nes_time_t time ) - { - if ( irq_pending ) - return time; - - if ( !(irq_ctr & 0x8000) ) - return no_irq; - - return 0x10000 - irq_ctr + last_time; - } - - virtual void run_until( nes_time_t end_time ) - { - long count = irq_ctr + (end_time - last_time); - if ( irq_ctr & 0x8000 ) - { - if ( count > 0xffff ) - { - count = 0xffff; - irq_pending = true; - } - } - else if ( count > 0x7fff ) - { - count = 0x7fff; - } - - irq_ctr = count; - last_time = end_time; - } - - virtual void end_frame( nes_time_t end_time ) - { - if ( end_time > last_time ) - run_until( end_time ); - last_time -= end_time; - assert( last_time >= 0 ); - sound.end_frame( end_time ); - } - - void write_bank( nes_addr_t, int data ); - void write_irq( nes_time_t, nes_addr_t, int data ); - - virtual int read( nes_time_t time, nes_addr_t addr ) - { - if ( addr == 0x4800 ) - return sound.read_data(); - - if ( addr == 0x5000 ) - { - irq_pending = false; - return irq_ctr & 0xff; - } - - if ( addr == 0x5800 ) - { - irq_pending = false; - return irq_ctr >> 8; - } - - return Nes_Mapper::read( time, addr ); - } - - virtual bool write_intercepted( nes_time_t time, nes_addr_t addr, int data ) - { - if ( addr == 0x4800 ) - { - sound.write_data( time, data ); - } - else if ( addr == 0x5000 ) - { - irq_ctr = (irq_ctr & 0xff00) | data; - irq_pending = false; - irq_changed(); - } - else if ( addr == 0x5800 ) - { - irq_ctr = (data << 8) | (irq_ctr & 0xff); - irq_pending = false; - irq_changed(); - } - else - { - return false; - } - - return true; - } - - virtual void write( nes_time_t, nes_addr_t addr, int data ) - { - int reg = addr >> 11 & 0x0F; - regs [reg] = data; - - int prg_bank = reg - 0x0c; - if ( (unsigned) prg_bank < 3 ) - { - if ( prg_bank == 0 && (data & 0x40) ) - mirror_vert(); - set_prg_bank( 0x8000 | (prg_bank << bank_8k), bank_8k, data & 0x3F ); - } - else if ( reg < 8 ) - { - set_chr_bank( reg * 0x400, bank_1k, data ); - } - else if ( reg < 0x0c ) - { - mirror_manual( regs [8] & 1, regs [9] & 1, regs [10] & 1, regs [11] & 1 ); - } - else - { - sound.write_addr( data ); - } - } -}; - -void register_namco106_mapper(); -void register_namco106_mapper() -{ - register_mapper( 19 ); -} - -// in the most obscure place in case crappy linker is used -void register_optional_mappers(); -void register_optional_mappers() -{ - extern void register_misc_mappers(); - register_misc_mappers(); - - extern void register_vrc6_mapper(); - register_vrc6_mapper(); - - //extern void register_mmc5_mapper(); - //register_mmc5_mapper(); - - extern void register_fme7_mapper(); - register_fme7_mapper(); - - extern void register_namco106_mapper(); - register_namco106_mapper(); - - extern void register_mmc24(); - register_mmc24(); -} - diff --git a/quicknes/nes_emu/Mapper_Vrc6.cpp b/quicknes/nes_emu/Mapper_Vrc6.cpp deleted file mode 100644 index 9cb2672c1c..0000000000 --- a/quicknes/nes_emu/Mapper_Vrc6.cpp +++ /dev/null @@ -1,262 +0,0 @@ - -// Konami VRC6 mapper - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -#include -#include "Nes_Vrc6_Apu.h" -#include "blargg_endian.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -struct vrc6_state_t -{ - // written registers - byte prg_16k_bank; - // could move sound regs int and out of vrc6_apu_state_t for state saving, - // allowing them to be stored here - byte old_sound_regs [3] [3]; // to do: eliminate this duplicate - byte mirroring; - byte prg_8k_bank; - byte chr_banks [8]; - byte irq_reload; - byte irq_mode; - - // internal state - BOOST::uint16_t next_time; - byte irq_pending; - byte unused; - - vrc6_apu_state_t sound_state; - - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (vrc6_state_t) == 26 + sizeof (vrc6_apu_state_t) ); - -void vrc6_state_t::swap() -{ - set_le16( &next_time, next_time ); - for ( unsigned i = 0; i < sizeof sound_state.delays / sizeof sound_state.delays [0]; i++ ) - set_le16( &sound_state.delays [i], sound_state.delays [i] ); -} - -class Mapper_Vrc6 : public Nes_Mapper, vrc6_state_t { - int swap_mask; - Nes_Vrc6_Apu sound; - enum { timer_period = 113 * 4 + 3 }; -public: - Mapper_Vrc6( int sm ) - { - swap_mask = sm; - vrc6_state_t* state = this; - register_state( state, sizeof *state ); - } - - virtual int channel_count() const { return sound.osc_count; } - - virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); } - - virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); } - - virtual void reset_state() - { - prg_8k_bank = last_bank - 1; - sound.reset(); - } - - virtual void save_state( mapper_state_t& out ) - { - sound.save_state( &sound_state ); - vrc6_state_t::swap(); - Nes_Mapper::save_state( out ); - vrc6_state_t::swap(); // to do: kind of hacky to swap in place - } - - virtual void read_state( mapper_state_t const& in ); - - virtual void apply_mapping() - { - enable_sram(); - set_prg_bank( 0x8000, bank_16k, prg_16k_bank ); - set_prg_bank( 0xC000, bank_8k, prg_8k_bank ); - - for ( int i = 0; i < (int) sizeof chr_banks; i++ ) - set_chr_bank( i * 0x400, bank_1k, chr_banks [i] ); - - write_bank( 0xb003, mirroring ); - } - - void reset_timer( nes_time_t present ) - { - next_time = present + unsigned ((0x100 - irq_reload) * timer_period) / 4; - } - - virtual void run_until( nes_time_t end_time ) - { - if ( irq_mode & 2 ) - { - while ( next_time < end_time ) - { - //dprintf( "%d timer expired\n", next_time ); - irq_pending = true; - reset_timer( next_time ); - } - } - } - - virtual void end_frame( nes_time_t end_time ) - { - run_until( end_time ); - - // to do: next_time might go negative if IRQ is disabled - next_time -= end_time; - - sound.end_frame( end_time ); - } - - virtual nes_time_t next_irq( nes_time_t present ) - { - if ( irq_pending ) - return present; - - if ( irq_mode & 2 ) - return next_time + 1; - - return no_irq; - } - - void write_bank( nes_addr_t, int data ); - void write_irq( nes_time_t, nes_addr_t, int data ); - - virtual void write( nes_time_t time, nes_addr_t addr, int data ) - { - int osc = unsigned (addr - sound.base_addr) / sound.addr_step; - - if ( (addr + 1) & 2 ) // optionally swap 1 and 2 - addr ^= swap_mask; - - int reg = addr & 3; - if ( (unsigned) osc < sound.osc_count && reg < sound.reg_count ) - sound.write_osc( time, osc, reg, data ); - else if ( addr < 0xf000 ) - write_bank( addr, data ); - else - write_irq( time, addr, data ); - } -}; - -void Mapper_Vrc6::read_state( mapper_state_t const& in ) -{ - Nes_Mapper::read_state( in ); - vrc6_state_t::swap(); - - // to do: eliminate when format is updated - // old-style registers - static char zero [sizeof old_sound_regs] = { 0 }; - if ( 0 != memcmp( old_sound_regs, zero, sizeof zero ) ) - { - dprintf( "Using old VRC6 sound register format\n" ); - memcpy( sound_state.regs, old_sound_regs, sizeof sound_state.regs ); - memset( old_sound_regs, 0, sizeof old_sound_regs ); - } - - sound.load_state( sound_state ); -} - -void Mapper_Vrc6::write_irq( nes_time_t time, nes_addr_t addr, int data ) -{ - // IRQ - run_until( time ); - //dprintf( "%d VRC6 IRQ [%d] = %02X\n", time, addr & 3, data ); - switch ( addr & 3 ) - { - case 0: - irq_reload = data; - break; - - case 1: - irq_pending = false; - irq_mode = data; - if ( data & 2 ) - reset_timer( time ); - break; - - case 2: - irq_pending = false; - irq_mode = (irq_mode & ~2) | ((irq_mode << 1) & 2); - break; - } - irq_changed(); -} - -void Mapper_Vrc6::write_bank( nes_addr_t addr, int data ) -{ - switch ( addr & 0xf003 ) - { - case 0x8000: - prg_16k_bank = data; - set_prg_bank( 0x8000, bank_16k, data ); - break; - - case 0xb003: { - mirroring = data; - - //dprintf( "Change mirroring %d\n", data ); -// emu()->enable_sram( data & 0x80 ); // to do: needed? - int page = data >> 5 & 1; - if ( data & 8 ) - mirror_single( ((data >> 2) ^ page) & 1 ); - else if ( data & 4 ) - mirror_horiz( page ); - else - mirror_vert( page ); - break; - } - - case 0xc000: - prg_8k_bank = data; - set_prg_bank( 0xC000, bank_8k, data ); - break; - - default: - int bank = (addr >> 11 & 4) | (addr & 3); - if ( addr >= 0xd000 ) - { - //dprintf( "change chr bank %d\n", bank ); - chr_banks [bank] = data; - set_chr_bank( bank * 0x400, bank_1k, data ); - } - break; - } -} - -static Nes_Mapper* make_vrc6a() -{ - return BLARGG_NEW Mapper_Vrc6( 0 ); -} - -static Nes_Mapper* make_vrc6b() -{ - return BLARGG_NEW Mapper_Vrc6( 3 ); -} - -void register_vrc6_mapper(); -void register_vrc6_mapper() -{ - Nes_Mapper::register_mapper( 24, make_vrc6a ); - Nes_Mapper::register_mapper( 26, make_vrc6b ); -} - diff --git a/quicknes/nes_emu/Mmc24.cpp b/quicknes/nes_emu/Mmc24.cpp deleted file mode 100644 index 13a7fba32f..0000000000 --- a/quicknes/nes_emu/Mmc24.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include - -#include "Nes_Mapper.h" -#include "blargg_source.h" - -#include "Mmc24.h" - - -class MMC2: public Nes_Mapper -{ - byte regs[6]; // A,B,C,D,E,F - - void mirror(byte val) - { - if (val & 1) - mirror_horiz(); - else - mirror_vert(); - } - -public: - MMC2() - { - register_state(regs, sizeof(regs)); - } - - virtual void reset_state() - { - std::memset(regs, 0, sizeof(regs)); - } - - virtual void apply_mapping() - { - mirror(regs[5]); - set_prg_bank(0x8000, bank_8k, regs[0]); - set_prg_bank(0xa000, bank_8k, 13); - set_prg_bank(0xc000, bank_8k, 14); - set_prg_bank(0xe000, bank_8k, 15); - - set_chr_bank(0x0000, bank_4k, regs[1]); - set_chr_bank(0x1000, bank_4k, regs[3]); - - set_chr_bank_ex(0x0000, bank_4k, regs[2]); - set_chr_bank_ex(0x1000, bank_4k, regs[4]); - } - - virtual void write(nes_time_t, nes_addr_t addr, int data) - { - switch (addr >> 12) - { - case 0xa: regs[0] = data; set_prg_bank(0x8000, bank_8k, data); break; - case 0xb: regs[1] = data; set_chr_bank(0x0000, bank_4k, data); break; - case 0xc: regs[2] = data; set_chr_bank_ex(0x0000, bank_4k, data); break; - case 0xd: regs[3] = data; set_chr_bank(0x1000, bank_4k, data); break; - case 0xe: regs[4] = data; set_chr_bank_ex(0x1000, bank_4k, data); break; - case 0xf: regs[5] = data; mirror(data); break; - } - } -}; - -class MMC4: public Nes_Mapper -{ - byte regs[6]; // A,B,C,D,E,F - - void mirror(byte val) - { - if (val & 1) - mirror_horiz(); - else - mirror_vert(); - } - -public: - MMC4() - { - register_state(regs, sizeof(regs)); - } - - virtual void reset_state() - { - std::memset(regs, 0, sizeof(regs)); - } - - virtual void apply_mapping() - { - enable_sram(); - - mirror(regs[5]); - set_prg_bank(0x8000, bank_16k, regs[0]); - - set_chr_bank(0x0000, bank_4k, regs[1]); - set_chr_bank(0x1000, bank_4k, regs[3]); - - set_chr_bank_ex(0x0000, bank_4k, regs[2]); - set_chr_bank_ex(0x1000, bank_4k, regs[4]); - } - - virtual void write(nes_time_t, nes_addr_t addr, int data) - { - switch (addr >> 12) - { - case 0xa: regs[0] = data; set_prg_bank(0x8000, bank_16k, data); break; - case 0xb: regs[1] = data; set_chr_bank(0x0000, bank_4k, data); break; - case 0xc: regs[2] = data; set_chr_bank_ex(0x0000, bank_4k, data); break; - case 0xd: regs[3] = data; set_chr_bank(0x1000, bank_4k, data); break; - case 0xe: regs[4] = data; set_chr_bank_ex(0x1000, bank_4k, data); break; - case 0xf: regs[5] = data; mirror(data); break; - } - } -}; - - -void register_mmc24() -{ - register_mapper(9); - register_mapper(10); -} diff --git a/quicknes/nes_emu/Mmc24.h b/quicknes/nes_emu/Mmc24.h deleted file mode 100644 index a7400ef0c9..0000000000 --- a/quicknes/nes_emu/Mmc24.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef MMC24_H -#define MMC24_H - - -void register_mmc24(); - -#endif diff --git a/quicknes/nes_emu/Multi_Buffer.cpp b/quicknes/nes_emu/Multi_Buffer.cpp deleted file mode 100644 index f6cf2057d9..0000000000 --- a/quicknes/nes_emu/Multi_Buffer.cpp +++ /dev/null @@ -1,213 +0,0 @@ - -// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ - -#include "Multi_Buffer.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) -{ - length_ = 0; - sample_rate_ = 0; - channels_changed_count_ = 1; -} - -blargg_err_t Multi_Buffer::set_channel_count( int ) -{ - return 0; -} - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ -} - -Mono_Buffer::~Mono_Buffer() -{ -} - -blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) -{ - RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} - -// Silent_Buffer - -Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse -{ - chan.left = NULL; - chan.center = NULL; - chan.right = NULL; -} - -// Mono_Buffer - -Mono_Buffer::channel_t Mono_Buffer::channel( int ) -{ - channel_t ch; - ch.center = &buf; - ch.left = &buf; - ch.right = &buf; - return ch; -} - -void Mono_Buffer::end_frame( blip_time_t t, bool ) -{ - buf.end_frame( t ); -} - -// Stereo_Buffer - -Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) -{ - chan.center = &bufs [0]; - chan.left = &bufs [1]; - chan.right = &bufs [2]; -} - -Stereo_Buffer::~Stereo_Buffer() -{ -} - -blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) -{ - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Stereo_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Stereo_Buffer::bass_freq( int bass ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( bass ); -} - -void Stereo_Buffer::clear() -{ - stereo_added = false; - was_stereo = false; - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].end_frame( clock_count ); - - stereo_added |= stereo; -} - -long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) -{ - require( !(count & 1) ); // count must be even - count = (unsigned) count / 2; - - long avail = bufs [0].samples_avail(); - if ( count > avail ) - count = avail; - if ( count ) - { - if ( stereo_added || was_stereo ) - { - mix_stereo( out, count ); - - bufs [0].remove_samples( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - else - { - mix_mono( out, count ); - - bufs [0].remove_samples( count ); - - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); - } - - // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) { - was_stereo = stereo_added; - stereo_added = false; - } - } - - return count * 2; -} - -void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) -{ - Blip_Reader left; - Blip_Reader right; - Blip_Reader center; - - left.begin( bufs [1] ); - right.begin( bufs [2] ); - int bass = center.begin( bufs [0] ); - - while ( count-- ) - { - int c = center.read(); - long l = c + left.read(); - long r = c + right.read(); - center.next( bass ); - out [0] = l; - out [1] = r; - out += 2; - - if ( (BOOST::int16_t) l != l ) - out [-2] = 0x7FFF - (l >> 24); - - left.next( bass ); - right.next( bass ); - - if ( (BOOST::int16_t) r != r ) - out [-1] = 0x7FFF - (r >> 24); - } - - center.end( bufs [0] ); - right.end( bufs [2] ); - left.end( bufs [1] ); -} - -void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) -{ - Blip_Reader in; - int bass = in.begin( bufs [0] ); - - while ( count-- ) - { - long s = in.read(); - in.next( bass ); - out [0] = s; - out [1] = s; - out += 2; - - if ( (BOOST::int16_t) s != s ) { - s = 0x7FFF - (s >> 24); - out [-2] = s; - out [-1] = s; - } - } - - in.end( bufs [0] ); -} - diff --git a/quicknes/nes_emu/Multi_Buffer.h b/quicknes/nes_emu/Multi_Buffer.h deleted file mode 100644 index bff3ac99fa..0000000000 --- a/quicknes/nes_emu/Multi_Buffer.h +++ /dev/null @@ -1,175 +0,0 @@ - -// Multi-channel sound buffer interface, and basic mono and stereo buffers - -// Blip_Buffer 0.4.0 - -#ifndef MULTI_BUFFER_H -#define MULTI_BUFFER_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -// Interface to one or more Blip_Buffers mapped to one or more channels -// consisting of left, center, and right buffers. -class Multi_Buffer { -public: - Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } - - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); - - // Get indexed channel, from 0 to channel count - 1 - struct channel_t { - Blip_Buffer* center; - Blip_Buffer* left; - Blip_Buffer* right; - }; - virtual channel_t channel( int index ) = 0; - - // See Blip_Buffer.h - virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; - virtual void clock_rate( long ) = 0; - virtual void bass_freq( int ) = 0; - virtual void clear() = 0; - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' - // if nothing was added to the left and right buffers of any channel for - // this time frame. - virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; - - // Number of samples per output frame (1 = mono, 2 = stereo) - int samples_per_frame() const; - - // Count of changes to channel configuration. Incremented whenever - // a change is made to any of the Blip_Buffers for any channel. - unsigned channels_changed_count() { return channels_changed_count_; } - - // See Blip_Buffer.h - virtual long read_samples( blip_sample_t*, long ) = 0; - virtual long samples_avail() const = 0; - -protected: - void channels_changed() { channels_changed_count_++; } -private: - // noncopyable - Multi_Buffer( const Multi_Buffer& ); - Multi_Buffer& operator = ( const Multi_Buffer& ); - - unsigned channels_changed_count_; - long sample_rate_; - int length_; - int const samples_per_frame_; -}; - -// Uses a single buffer and outputs mono samples. -class Mono_Buffer : public Multi_Buffer { - Blip_Buffer buf; -public: - Mono_Buffer(); - ~Mono_Buffer(); - - // Buffer used for all channels - Blip_Buffer* center() { return &buf; } - - // See Multi_Buffer - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); -}; - -// Uses three buffers (one for center) and outputs stereo sample pairs. -class Stereo_Buffer : public Multi_Buffer { -public: - Stereo_Buffer(); - ~Stereo_Buffer(); - - // Buffers used for all channels - Blip_Buffer* center() { return &bufs [0]; } - Blip_Buffer* left() { return &bufs [1]; } - Blip_Buffer* right() { return &bufs [2]; } - - // See Multi_Buffer - blargg_err_t set_sample_rate( long, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int index ); - void end_frame( blip_time_t, bool added_stereo = true ); - - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; - channel_t chan; - bool stereo_added; - bool was_stereo; - - void mix_stereo( blip_sample_t*, long ); - void mix_mono( blip_sample_t*, long ); -}; - -// Silent_Buffer generates no samples, useful where no sound is wanted -class Silent_Buffer : public Multi_Buffer { - channel_t chan; -public: - Silent_Buffer(); - - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long ) { } - void bass_freq( int ) { } - void clear() { } - channel_t channel( int ) { return chan; } - void end_frame( blip_time_t, bool unused = true ) { } - long samples_avail() const { return 0; } - long read_samples( blip_sample_t*, long ) { return 0; } -}; - - -// End of public interface - -inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return 0; -} - -inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) -{ - return Multi_Buffer::set_sample_rate( rate, msec ); -} - -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } - -inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; } - -inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int ) { return chan; } - -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } - -inline int Multi_Buffer::length() const { return length_; } - -inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } - -inline void Mono_Buffer::clear() { buf.clear(); } - -inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } - -inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - -inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } - -#endif - diff --git a/quicknes/nes_emu/Nes_Apu.cpp b/quicknes/nes_emu/Nes_Apu.cpp deleted file mode 100644 index d9d362d6cb..0000000000 --- a/quicknes/nes_emu/Nes_Apu.cpp +++ /dev/null @@ -1,380 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -int const amp_range = 15; - -Nes_Apu::Nes_Apu() : - square1( &square_synth ), - square2( &square_synth ) -{ - dmc.apu = this; - dmc.prg_reader = NULL; - irq_notifier_ = NULL; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = ▵ - oscs [3] = &noise; - oscs [4] = &dmc; - - output( NULL ); - volume( 1.0 ); - reset( false ); -} - -Nes_Apu::~Nes_Apu() -{ -} - -void Nes_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - triangle.synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); - dmc.synth.treble_eq( eq ); -} - -void Nes_Apu::enable_nonlinear( double v ) -{ - dmc.nonlinear = true; - square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); - - const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); - triangle.synth.volume( 3.0 * tnd ); - noise.synth.volume( 2.0 * tnd ); - dmc.synth.volume( tnd ); - - square1 .last_amp = 0; - square2 .last_amp = 0; - triangle.last_amp = 0; - noise .last_amp = 0; - dmc .last_amp = 0; -} - -void Nes_Apu::volume( double v ) -{ - dmc.nonlinear = false; - square_synth.volume( 0.1128 / amp_range * v ); - triangle.synth.volume( 0.12765 / amp_range * v ); - noise.synth.volume( 0.0741 / amp_range * v ); - dmc.synth.volume( 0.42545 / 127 * v ); -} - -void Nes_Apu::output( Blip_Buffer* buffer ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buffer ); -} - -void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) -{ - // to do: time pal frame periods exactly - frame_period = pal_mode ? 8314 : 7458; - dmc.pal_mode = pal_mode; - - square1.reset(); - square2.reset(); - triangle.reset(); - noise.reset(); - dmc.reset(); - - last_time = 0; - last_dmc_time = 0; - osc_enables = 0; - irq_flag = false; - earliest_irq_ = no_irq; - frame_delay = 1; - write_register( 0, 0x4017, 0x00 ); - write_register( 0, 0x4015, 0x00 ); - - for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) - write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); - - dmc.dac = initial_dmc_dac; - if ( !dmc.nonlinear ) - triangle.last_amp = 15; - //if ( !dmc.nonlinear ) // to do: remove? - // dmc.last_amp = initial_dmc_dac; // prevent output transition -} - -void Nes_Apu::irq_changed() -{ - nes_time_t new_irq = dmc.next_irq; - if ( dmc.irq_flag | irq_flag ) { - new_irq = 0; - } - else if ( new_irq > next_irq ) { - new_irq = next_irq; - } - - if ( new_irq != earliest_irq_ ) { - earliest_irq_ = new_irq; - if ( irq_notifier_ ) - irq_notifier_( irq_data ); - } -} - -// frames - -void Nes_Apu::run_until( nes_time_t end_time ) -{ - require( end_time >= last_dmc_time ); - if ( end_time > next_dmc_read_time() ) - { - nes_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } -} - -void Nes_Apu::run_until_( nes_time_t end_time ) -{ - require( end_time >= last_time ); - - if ( end_time == last_time ) - return; - - if ( last_dmc_time < end_time ) - { - nes_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } - - while ( true ) - { - // earlier of next frame time or end time - nes_time_t time = last_time + frame_delay; - if ( time > end_time ) - time = end_time; - frame_delay -= time - last_time; - - // run oscs to present - square1.run( last_time, time ); - square2.run( last_time, time ); - triangle.run( last_time, time ); - noise.run( last_time, time ); - last_time = time; - - if ( time == end_time ) - break; // no more frames to run - - // take frame-specific actions - frame_delay = frame_period; - switch ( frame++ ) - { - case 0: - if ( !(frame_mode & 0xc0) ) { - next_irq = time + frame_period * 4 + 1; - irq_flag = true; - } - // fall through - case 2: - // clock length and sweep on frames 0 and 2 - square1.clock_length( 0x20 ); - square2.clock_length( 0x20 ); - noise.clock_length( 0x20 ); - triangle.clock_length( 0x80 ); // different bit for halt flag on triangle - - square1.clock_sweep( -1 ); - square2.clock_sweep( 0 ); - break; - - case 1: - // frame 1 is slightly shorter - frame_delay -= 2; - break; - - case 3: - frame = 0; - - // frame 3 is almost twice as long in mode 1 - if ( frame_mode & 0x80 ) - frame_delay += frame_period - 6; - break; - } - - // clock envelopes and linear counter every frame - triangle.clock_linear_counter(); - square1.clock_envelope(); - square2.clock_envelope(); - noise.clock_envelope(); - } -} - -template -inline void zero_apu_osc( T* osc, nes_time_t time ) -{ - Blip_Buffer* output = osc->output; - int last_amp = osc->last_amp; - osc->last_amp = 0; - if ( output && last_amp ) - osc->synth.offset( time, -last_amp, output ); -} - -void Nes_Apu::end_frame( nes_time_t end_time ) -{ - if ( end_time > last_time ) - run_until_( end_time ); - - if ( dmc.nonlinear ) - { - zero_apu_osc( &square1, last_time ); - zero_apu_osc( &square2, last_time ); - zero_apu_osc( &triangle, last_time ); - zero_apu_osc( &noise, last_time ); - zero_apu_osc( &dmc, last_time ); - } - - // make times relative to new frame - last_time -= end_time; - require( last_time >= 0 ); - - last_dmc_time -= end_time; - require( last_dmc_time >= 0 ); - - if ( next_irq != no_irq ) { - next_irq -= end_time; - assert( next_irq >= 0 ); - } - if ( dmc.next_irq != no_irq ) { - dmc.next_irq -= end_time; - assert( dmc.next_irq >= 0 ); - } - if ( earliest_irq_ != no_irq ) { - earliest_irq_ -= end_time; - if ( earliest_irq_ < 0 ) - earliest_irq_ = 0; - } -} - -// registers - -static const unsigned char length_table [0x20] = { - 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, - 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, - 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, - 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E -}; - -void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) -{ - require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) - require( (unsigned) data <= 0xff ); - - // Ignore addresses outside range - if ( addr < start_addr || end_addr < addr ) - return; - - run_until_( time ); - - if ( addr < 0x4014 ) - { - // Write to channel - int osc_index = (addr - start_addr) >> 2; - Nes_Osc* osc = oscs [osc_index]; - - int reg = addr & 3; - osc->regs [reg] = data; - osc->reg_written [reg] = true; - - if ( osc_index == 4 ) - { - // handle DMC specially - dmc.write_register( reg, data ); - } - else if ( reg == 3 ) - { - // load length counter - if ( (osc_enables >> osc_index) & 1 ) - osc->length_counter = length_table [(data >> 3) & 0x1f]; - - // reset square phase - if ( osc_index < 2 ) - ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; - } - } - else if ( addr == 0x4015 ) - { - // Channel enables - for ( int i = osc_count; i--; ) - if ( !((data >> i) & 1) ) - oscs [i]->length_counter = 0; - - bool recalc_irq = dmc.irq_flag; - dmc.irq_flag = false; - - int old_enables = osc_enables; - osc_enables = data; - if ( !(data & 0x10) ) { - dmc.next_irq = no_irq; - recalc_irq = true; - } - else if ( !(old_enables & 0x10) ) { - dmc.start(); // dmc just enabled - } - - if ( recalc_irq ) - irq_changed(); - } - else if ( addr == 0x4017 ) - { - // Frame mode - frame_mode = data; - - bool irq_enabled = !(data & 0x40); - irq_flag &= irq_enabled; - next_irq = no_irq; - - // mode 1 - frame_delay = (frame_delay & 1); - frame = 0; - - if ( !(data & 0x80) ) - { - // mode 0 - frame = 1; - frame_delay += frame_period; - if ( irq_enabled ) - next_irq = time + frame_delay + frame_period * 3; - } - - irq_changed(); - } -} - -int Nes_Apu::read_status( nes_time_t time ) -{ - run_until_( time - 1 ); - - int result = (dmc.irq_flag << 7) | (irq_flag << 6); - - for ( int i = 0; i < osc_count; i++ ) - if ( oscs [i]->length_counter ) - result |= 1 << i; - - run_until_( time ); - - if ( irq_flag ) { - irq_flag = false; - irq_changed(); - } - - return result; -} - diff --git a/quicknes/nes_emu/Nes_Apu.h b/quicknes/nes_emu/Nes_Apu.h deleted file mode 100644 index a004ff757e..0000000000 --- a/quicknes/nes_emu/Nes_Apu.h +++ /dev/null @@ -1,177 +0,0 @@ - -// NES 2A03 APU sound chip emulator - -// Nes_Snd_Emu 0.1.7 - -#ifndef NES_APU_H -#define NES_APU_H - -typedef long nes_time_t; // CPU clock cycle count -typedef unsigned nes_addr_t; // 16-bit memory address - -#include "Nes_Oscs.h" - -struct apu_state_t; -class Nes_Buffer; - -class Nes_Apu { -public: - Nes_Apu(); - ~Nes_Apu(); - - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Set memory reader callback used by DMC oscillator to fetch samples. - // When callback is invoked, 'user_data' is passed unchanged as the - // first parameter. - void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); - - // All time values are the number of CPU clock cycles relative to the - // beginning of the current time frame. Before resetting the CPU clock - // count, call end_frame( last_cpu_time ). - - // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; - void write_register( nes_time_t, nes_addr_t, int data ); - - // Read from status register at 0x4015 - enum { status_addr = 0x4015 }; - int read_status( nes_time_t ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new time frame at time 0. Time frames have no effect on emulation - // and each can be whatever length is convenient. - void end_frame( nes_time_t ); - -// Additional optional features (can be ignored without any problem) - - // Reset internal frame counter, registers, and all oscillators. - // Use PAL timing if pal_timing is true, otherwise use NTSC timing. - // Set the DMC oscillator's initial DAC value to initial_dmc_dac without - // any audible click. - void reset( bool pal_timing = false, int initial_dmc_dac = 0 ); - - // Save/load exact emulation state - void save_state( apu_state_t* out ) const; - void load_state( apu_state_t const& ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Set treble equalization (see notes.txt) - void treble_eq( const blip_eq_t& ); - - // Set sound output of specific oscillator to buffer. If buffer is NULL, - // the specified oscillator is muted and emulation accuracy is reduced. - // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, - // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* buffer ); - - // Set IRQ time callback that is invoked when the time of earliest IRQ - // may have changed, or NULL to disable. When callback is invoked, - // 'user_data' is passed unchanged as the first parameter. - void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - - // Get time that APU-generated IRQ will occur if no further register reads - // or writes occur. If IRQ is already pending, returns irq_waiting. If no - // IRQ will occur, returns no_irq. - enum { no_irq = LONG_MAX / 2 + 1 }; - enum { irq_waiting = 0 }; - nes_time_t earliest_irq( nes_time_t ) const; - - // Count number of DMC reads that would occur if 'run_until( t )' were executed. - // If last_read is not NULL, set *last_read to the earliest time that - // 'count_dmc_reads( time )' would result in the same result. - int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; - - // Time when next DMC memory read will occur - nes_time_t next_dmc_read_time() const; - - // Run DMC until specified time, so that any DMC memory reads can be - // accounted for (i.e. inserting CPU wait states). - void run_until( nes_time_t ); - -// End of public interface. -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); - static double nonlinear_tnd_gain() { return 0.75; } -private: - friend struct Nes_Dmc; - - // noncopyable - Nes_Apu( const Nes_Apu& ); - Nes_Apu& operator = ( const Nes_Apu& ); - - Nes_Osc* oscs [osc_count]; - Nes_Square square1; - Nes_Square square2; - Nes_Noise noise; - Nes_Triangle triangle; - Nes_Dmc dmc; - - nes_time_t last_time; // has been run until this time in current frame - nes_time_t last_dmc_time; - nes_time_t earliest_irq_; - nes_time_t next_irq; - int frame_period; - int frame_delay; // cycles until frame counter runs next - int frame; // current frame (0-3) - int osc_enables; - int frame_mode; - bool irq_flag; - void (*irq_notifier_)( void* user_data ); - void* irq_data; - Nes_Square::Synth square_synth; // shared by squares - - void irq_changed(); - void state_restored(); - void run_until_( nes_time_t ); - - // TODO: remove - friend class Nes_Core; -}; - -inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) -{ - assert( (unsigned) osc < osc_count ); - oscs [osc]->output = buf; -} - -inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const -{ - return earliest_irq_; -} - -inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) -{ - dmc.prg_reader_data = user_data; - dmc.prg_reader = func; -} - -inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) -{ - irq_notifier_ = func; - irq_data = user_data; -} - -inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const -{ - return dmc.count_reads( time, last_read ); -} - -inline nes_time_t Nes_Dmc::next_read_time() const -{ - if ( length_counter == 0 ) - return Nes_Apu::no_irq; // not reading - - return apu->last_dmc_time + delay + long (bits_remain - 1) * period; -} - -inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } - -#endif - diff --git a/quicknes/nes_emu/Nes_Buffer.cpp b/quicknes/nes_emu/Nes_Buffer.cpp deleted file mode 100644 index 97d76888e3..0000000000 --- a/quicknes/nes_emu/Nes_Buffer.cpp +++ /dev/null @@ -1,201 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/libs/ - -#include "Nes_Buffer.h" - -#include "Nes_Apu.h" - -/* Library Copyright (C) 2003-2006 Shay Green. 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 -module is distributed in the hope that 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 "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -// Nes_Buffer - -Nes_Buffer::Nes_Buffer() : Multi_Buffer( 1 ) { } - -Nes_Buffer::~Nes_Buffer() { } - -Multi_Buffer* set_apu( Nes_Buffer* buf, Nes_Apu* apu ) -{ - buf->set_apu( apu ); - return buf; -} - -void Nes_Buffer::enable_nonlinearity( bool b ) -{ - if ( b ) - clear(); - - Nes_Apu* apu = nonlin.enable( b, &tnd ); - apu->osc_output( 0, &buf ); - apu->osc_output( 1, &buf ); -} - -blargg_err_t Nes_Buffer::set_sample_rate( long rate, int msec ) -{ - enable_nonlinearity( nonlin.enabled ); // reapply - RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - RETURN_ERR( tnd.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} - -void Nes_Buffer::clock_rate( long rate ) -{ - buf.clock_rate( rate ); - tnd.clock_rate( rate ); -} - -void Nes_Buffer::bass_freq( int freq ) -{ - buf.bass_freq( freq ); - tnd.bass_freq( freq ); -} - -void Nes_Buffer::clear() -{ - nonlin.clear(); - buf.clear(); - tnd.clear(); -} - -Nes_Buffer::channel_t Nes_Buffer::channel( int i ) -{ - channel_t c; - c.center = &buf; - if ( 2 <= i && i <= 4 ) - c.center = &tnd; // only use for triangle, noise, and dmc - c.left = c.center; - c.right = c.center; - return c; -} - -void Nes_Buffer::end_frame( blip_time_t length, bool ) -{ - buf.end_frame( length ); - tnd.end_frame( length ); -} - -long Nes_Buffer::samples_avail() const -{ - return buf.samples_avail(); -} - -long Nes_Buffer::read_samples( blip_sample_t* out, long count ) -{ - count = nonlin.make_nonlinear( tnd, count ); - if ( count ) - { - Blip_Reader lin; - Blip_Reader nonlin; - - int lin_bass = lin.begin( buf ); - int nonlin_bass = nonlin.begin( tnd ); - - for ( int n = count; n--; ) - { - int s = lin.read() + nonlin.read(); - lin.next( lin_bass ); - nonlin.next( nonlin_bass ); - *out++ = s; - - if ( (BOOST::int16_t) s != s ) - out [-1] = 0x7FFF - (s >> 24); - } - - lin.end( buf ); - nonlin.end( tnd ); - - buf.remove_samples( count ); - tnd.remove_samples( count ); - } - - return count; -} - -// Nes_Nonlinearizer - -Nes_Nonlinearizer::Nes_Nonlinearizer() -{ - apu = NULL; - enabled = true; - - float const gain = 0x7fff * 1.3f; - // don't use entire range, so any overflow will stay within table - int const range = (int) (table_size * Nes_Apu::nonlinear_tnd_gain()); - for ( int i = 0; i < table_size; i++ ) - { - int const offset = table_size - range; - int j = i - offset; - float n = 202.0f / (range - 1) * j; - float d = gain * 163.67f / (24329.0f / n + 100.0f); - int out = (int) d; -//out = j << (15 - table_bits); // make table linear for testing - assert( out < 0x8000 ); - table [j & (table_size - 1)] = out; - } -} - -Nes_Apu* Nes_Nonlinearizer::enable( bool b, Blip_Buffer* buf ) -{ - require( apu ); - apu->osc_output( 2, buf ); - apu->osc_output( 3, buf ); - apu->osc_output( 4, buf ); - enabled = b; - if ( b ) - apu->enable_nonlinear( 1.0 ); - else - apu->volume( 1.0 ); - return apu; -} - -#define ENTRY( s ) table [(s) >> (blip_sample_bits - table_bits - 1) & (table_size - 1)] - -long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count ) -{ - require( apu ); - long avail = buf.samples_avail(); - if ( count > avail ) - count = avail; - if ( count && enabled ) - { - - Blip_Buffer::buf_t_* p = buf.buffer_; - long accum = this->accum; - long prev = this->prev; - for ( unsigned n = count; n; --n ) - { - long entry = ENTRY( accum ); - check( (entry >= 0) == (accum >= 0) ); - accum += *p; - *p++ = (entry - prev) << (blip_sample_bits - 16); - prev = entry; - } - - this->prev = prev; - this->accum = accum; - } - - return count; -} - -void Nes_Nonlinearizer::clear() -{ - accum = 0; - prev = ENTRY( 86016000 ); // avoid thump due to APU's triangle dc bias - // TODO: still results in slight clicks and thumps -} - diff --git a/quicknes/nes_emu/Nes_Buffer.h b/quicknes/nes_emu/Nes_Buffer.h deleted file mode 100644 index 8e47a4d9ce..0000000000 --- a/quicknes/nes_emu/Nes_Buffer.h +++ /dev/null @@ -1,68 +0,0 @@ - -// NES non-linear audio buffer - -// Nes_Emu 0.7.0 - -#ifndef NES_BUFFER_H -#define NES_BUFFER_H - -#include "Multi_Buffer.h" -class Nes_Apu; - -class Nes_Nonlinearizer { -private: - enum { table_bits = 11 }; - enum { table_size = 1 << table_bits }; - BOOST::int16_t table [table_size]; - Nes_Apu* apu; - long accum; - long prev; - -public: - Nes_Nonlinearizer(); - bool enabled; - void clear(); - void set_apu( Nes_Apu* a ) { apu = a; } - Nes_Apu* enable( bool, Blip_Buffer* tnd ); - long make_nonlinear( Blip_Buffer& buf, long count ); -}; - -class Nes_Buffer : public Multi_Buffer { -public: - Nes_Buffer(); - ~Nes_Buffer(); - - // Setup APU for use with buffer, including setting its output to this buffer. - // If you're using Nes_Emu, this is automatically called for you. - void set_apu( Nes_Apu* apu ) { nonlin.set_apu( apu ); } - - // Enable/disable non-linear output - void enable_nonlinearity( bool = true ); - - // Blip_Buffer to output other sound chips to - Blip_Buffer* buffer() { return &buf; } - - // See Multi_Buffer.h - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - -#if 0 // What is this? - Multi_Buffer::sample_rate; -#endif - - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - Blip_Buffer buf; - Blip_Buffer tnd; - Nes_Nonlinearizer nonlin; - friend Multi_Buffer* set_apu( Nes_Buffer*, Nes_Apu* ); -}; - -#endif - diff --git a/quicknes/nes_emu/Nes_Cart.cpp b/quicknes/nes_emu/Nes_Cart.cpp deleted file mode 100644 index d006ec80f9..0000000000 --- a/quicknes/nes_emu/Nes_Cart.cpp +++ /dev/null @@ -1,268 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Cart.h" - -#include -#include - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -char const Nes_Cart::not_ines_file [] = "Not an iNES file"; - -Nes_Cart::Nes_Cart() -{ - prg_ = NULL; - chr_ = NULL; - clear(); -} - -Nes_Cart::~Nes_Cart() -{ - clear(); -} - -void Nes_Cart::clear() -{ - free( prg_ ); - prg_ = NULL; - - free( chr_ ); - chr_ = NULL; - - prg_size_ = 0; - chr_size_ = 0; - mapper = 0; -} - -long Nes_Cart::round_to_bank_size( long n ) -{ - n += bank_size - 1; - return n - n % bank_size; -} - -blargg_err_t Nes_Cart::resize_prg( long size ) -{ - if ( size != prg_size_ ) - { - // padding allows CPU to always read operands of instruction, which - // might go past end of data - void* p = realloc( prg_, round_to_bank_size( size ) + 2 ); - CHECK_ALLOC( p || !size ); - prg_ = (byte*) p; - prg_size_ = size; - } - return 0; -} - -blargg_err_t Nes_Cart::resize_chr( long size ) -{ - if ( size != chr_size_ ) - { - void* p = realloc( chr_, round_to_bank_size( size ) ); - CHECK_ALLOC( p || !size ); - chr_ = (byte*) p; - chr_size_ = size; - } - return 0; -} - -// iNES reading - -struct ines_header_t { - BOOST::uint8_t signature [4]; - BOOST::uint8_t prg_count; // number of 16K PRG banks - BOOST::uint8_t chr_count; // number of 8K CHR banks - BOOST::uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror - BOOST::uint8_t flags2; // MMMM --XX Mapper high 4 bits - BOOST::uint8_t zero [8]; // if zero [7] is non-zero, treat flags2 as zero -}; -BOOST_STATIC_ASSERT( sizeof (ines_header_t) == 16 ); - -blargg_err_t Nes_Cart::load_ines( Auto_File_Reader in ) -{ - RETURN_ERR( in.open() ); - - ines_header_t h; - RETURN_ERR( in->read( &h, sizeof h ) ); - - if ( 0 != memcmp( h.signature, "NES\x1A", 4 ) ) - return not_ines_file; - - if ( h.zero [7] ) // handle header defaced by a fucking idiot's handle - h.flags2 = 0; - - set_mapper( h.flags, h.flags2 ); - - if ( h.flags & 0x04 ) // skip trainer - RETURN_ERR( in->skip( 512 ) ); - - RETURN_ERR( resize_prg( h.prg_count * 16 * 1024L ) ); - RETURN_ERR( resize_chr( h.chr_count * 8 * 1024L ) ); - - RETURN_ERR( in->read( prg(), prg_size() ) ); - RETURN_ERR( in->read( chr(), chr_size() ) ); - - return 0; -} - -// IPS patching - -// IPS patch file format (integers are big-endian): -// 5 "PATCH" -// n blocks -// -// normal block: -// 3 offset -// 2 size -// n data -// -// repeated byte block: -// 3 offset -// 2 0 -// 2 size -// 1 fill value -// -// end block (optional): -// 3 "EOF" -// -// A block can append data to the file by specifying an offset at the end of -// the current file data. - -typedef BOOST::uint8_t byte; -static blargg_err_t apply_ips_patch( Data_Reader& patch, byte** file, long* file_size ) -{ - byte signature [5]; - RETURN_ERR( patch.read( signature, sizeof signature ) ); - if ( memcmp( signature, "PATCH", sizeof signature ) ) - return "Not an IPS patch file"; - - while ( patch.remain() ) - { - // read offset - byte buf [6]; - RETURN_ERR( patch.read( buf, 3 ) ); - long offset = buf [0] * 0x10000 + buf [1] * 0x100 + buf [2]; - if ( offset == 0x454F46 ) // 'EOF' - break; - - // read size - RETURN_ERR( patch.read( buf, 2 ) ); - long size = buf [0] * 0x100 + buf [1]; - - // size = 0 signals a run of identical bytes - int fill = -1; - if ( size == 0 ) - { - RETURN_ERR( patch.read( buf, 3 ) ); - size = buf [0] * 0x100 + buf [1]; - fill = buf [2]; - } - - // expand file if new data is at exact end of file - if ( offset == *file_size ) - { - *file_size = offset + size; - void* p = realloc( *file, *file_size ); - CHECK_ALLOC( p ); - *file = (byte*) p; - } - - //dprintf( "Patch offset: 0x%04X, size: 0x%04X\n", (int) offset, (int) size ); - - if ( offset < 0 || *file_size < offset + size ) - return "IPS tried to patch past end of file"; - - // read/fill data - if ( fill < 0 ) - RETURN_ERR( patch.read( *file + offset, size ) ); - else - memset( *file + offset, fill, size ); - } - - return 0; -} - -blargg_err_t Nes_Cart::load_patched_ines( Auto_File_Reader in, Auto_File_Reader patch ) -{ - RETURN_ERR( in.open() ); - RETURN_ERR( patch.open() ); - - // read file into memory - long size = in->remain(); - byte* ines = (byte*) malloc( size ); - CHECK_ALLOC( ines ); - const char* err = in->read( ines, size ); - - // apply patch - if ( !err ) - err = apply_ips_patch( *patch, &ines, &size ); - - // load patched file - if ( !err ) - { - Mem_File_Reader patched( ines, size ); - err = load_ines( patched ); - } - - free( ines ); - - return err; -} - -blargg_err_t Nes_Cart::apply_ips_to_prg( Auto_File_Reader patch ) -{ - RETURN_ERR( patch.open() ); - - long size = prg_size(); - - byte* prg_copy = (byte*) malloc( size ); - CHECK_ALLOC( prg_copy ); - memcpy( prg_copy, prg(), size ); - - const char* err = apply_ips_patch( *patch, &prg_copy, &size ); - - if ( !err ) - { - resize_prg( size ); - memcpy( prg(), prg_copy, size ); - } - - free( prg_copy ); - - return err; -} - -blargg_err_t Nes_Cart::apply_ips_to_chr( Auto_File_Reader patch ) -{ - RETURN_ERR( patch.open() ); - - long size = chr_size(); - - byte* chr_copy = (byte*) malloc( size ); - CHECK_ALLOC( chr_copy ); - memcpy( chr_copy, chr(), size ); - - const char* err = apply_ips_patch( *patch, &chr_copy, &size ); - - if ( !err ) - { - resize_chr( size ); - memcpy( chr(), chr_copy, size ); - } - - free( chr_copy ); - - return err; -} diff --git a/quicknes/nes_emu/Nes_Cart.h b/quicknes/nes_emu/Nes_Cart.h deleted file mode 100644 index f00aac0979..0000000000 --- a/quicknes/nes_emu/Nes_Cart.h +++ /dev/null @@ -1,93 +0,0 @@ - -// NES cartridge data (PRG, CHR, mapper) - -// Nes_Emu 0.7.0 - -#ifndef NES_CART_H -#define NES_CART_H - -#include "blargg_common.h" -#include "abstract_file.h" - -class Nes_Cart { - typedef BOOST::uint8_t byte; -public: - Nes_Cart(); - ~Nes_Cart(); - - // Load iNES file - blargg_err_t load_ines( Auto_File_Reader ); - static const char not_ines_file []; - - // Load iNES file and apply IPS patch - blargg_err_t load_patched_ines( Auto_File_Reader, Auto_File_Reader ips_patch ); - - // Apply IPS patches to specific parts - blargg_err_t apply_ips_to_prg( Auto_File_Reader ips_patch ); - blargg_err_t apply_ips_to_chr( Auto_File_Reader ips_patch ); - - // to do: support UNIF? - - // True if data is currently loaded - bool loaded() const { return prg_ != NULL; } - - // Free data - void clear(); - - // True if cartridge claims to have battery-backed memory - bool has_battery_ram() const; - - // Size of PRG data - long prg_size() const { return prg_size_; } - - // Size of CHR data - long chr_size() const { return chr_size_; } - - // Change size of PRG (code) data - blargg_err_t resize_prg( long ); - - // Change size of CHR (graphics) data - blargg_err_t resize_chr( long ); - - // Set mapper and information bytes. LSB and MSB are the standard iNES header - // bytes at offsets 6 and 7. - void set_mapper( int mapper_lsb, int mapper_msb ); - - unsigned mapper_data() const { return mapper; } - - // Initial mirroring setup - int mirroring() const { return mapper & 0x09; } - - // iNES mapper code - int mapper_code() const; - - // Pointer to beginning of PRG data - byte * prg() { return prg_; } - byte const* prg() const { return prg_; } - - // Pointer to beginning of CHR data - byte * chr() { return chr_; } - byte const* chr() const { return chr_; } - - // End of public interface -private: - enum { bank_size = 8 * 1024L }; // bank sizes must be a multiple of this - byte* prg_; - byte* chr_; - long prg_size_; - long chr_size_; - unsigned mapper; - long round_to_bank_size( long n ); -}; - -inline bool Nes_Cart::has_battery_ram() const { return mapper & 0x02; } - -inline void Nes_Cart::set_mapper( int mapper_lsb, int mapper_msb ) -{ - mapper = mapper_msb * 0x100 + mapper_lsb; -} - -inline int Nes_Cart::mapper_code() const { return ((mapper >> 8) & 0xf0) | ((mapper >> 4) & 0x0f); } - -#endif - diff --git a/quicknes/nes_emu/Nes_Core.cpp b/quicknes/nes_emu/Nes_Core.cpp deleted file mode 100644 index e79d3e89cf..0000000000 --- a/quicknes/nes_emu/Nes_Core.cpp +++ /dev/null @@ -1,572 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Core.h" - -#include -#include "Nes_Mapper.h" -#include "Nes_State.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -extern const char unsupported_mapper [] = "Unsupported mapper"; - -bool const wait_states_enabled = true; -bool const single_instruction_mode = false; // for debugging irq/nmi timing issues - -const int unmapped_fill = Nes_Cpu::page_wrap_opcode; - -unsigned const low_ram_size = 0x800; -unsigned const low_ram_end = 0x2000; -unsigned const sram_end = 0x8000; - -Nes_Core::Nes_Core() : ppu( this ) -{ - cart = NULL; - impl = NULL; - mapper = NULL; - memset( &nes, 0, sizeof nes ); - memset( &joypad, 0, sizeof joypad ); -} - -blargg_err_t Nes_Core::init() -{ - if ( !impl ) - { - CHECK_ALLOC( impl = BLARGG_NEW impl_t ); - impl->apu.dmc_reader( read_dmc, this ); - impl->apu.irq_notifier( apu_irq_changed, this ); - } - - return 0; -} - -void Nes_Core::close() -{ - // check that nothing modified unmapped page - #ifndef NDEBUG - //if ( cart && mem_differs( impl->unmapped_page, unmapped_fill, sizeof impl->unmapped_page ) ) - // dprintf( "Unmapped code page was written to\n" ); - #endif - - cart = NULL; - delete mapper; - mapper = NULL; - - ppu.close_chr(); - - disable_rendering(); -} - -blargg_err_t Nes_Core::open( Nes_Cart const* new_cart ) -{ - close(); - - RETURN_ERR( init() ); - - mapper = Nes_Mapper::create( new_cart, this ); - if ( !mapper ) - return unsupported_mapper; - - RETURN_ERR( ppu.open_chr( new_cart->chr(), new_cart->chr_size() ) ); - - cart = new_cart; - memset( impl->unmapped_page, unmapped_fill, sizeof impl->unmapped_page ); - reset( true, true ); - return 0; -} - -Nes_Core::~Nes_Core() -{ - close(); - delete impl; -} - -void Nes_Core::save_state( Nes_State_* out ) const -{ - out->clear(); - - out->nes = nes; - out->nes_valid = true; - - *out->cpu = cpu::r; - out->cpu_valid = true; - - *out->joypad = joypad; - out->joypad_valid = true; - - impl->apu.save_state( out->apu ); - out->apu_valid = true; - - ppu.save_state( out ); - - memcpy( out->ram, cpu::low_mem, out->ram_size ); - out->ram_valid = true; - - out->sram_size = 0; - if ( sram_present ) - { - out->sram_size = sizeof impl->sram; - memcpy( out->sram, impl->sram, out->sram_size ); - } - - out->mapper->size = 0; - mapper->save_state( *out->mapper ); - out->mapper_valid = true; -} - -void Nes_Core::save_state( Nes_State* out ) const -{ - save_state( reinterpret_cast(out) ); -} - -void Nes_Core::load_state( Nes_State_ const& in ) -{ - require( cart ); - - disable_rendering(); - error_count = 0; - - if ( in.nes_valid ) - nes = in.nes; - - // always use frame count - ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over - nes.frame_count = in.nes.frame_count; - if ( (frame_count_t) nes.frame_count == invalid_frame_count ) - nes.frame_count = 0; - - if ( in.cpu_valid ) - cpu::r = *in.cpu; - - if ( in.joypad_valid ) - joypad = *in.joypad; - - if ( in.apu_valid ) - { - impl->apu.load_state( *in.apu ); - // prevent apu from running extra at beginning of frame - impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock ); - } - else - { - impl->apu.reset(); - } - - ppu.load_state( in ); - - if ( in.ram_valid ) - memcpy( cpu::low_mem, in.ram, in.ram_size ); - - sram_present = false; - if ( in.sram_size ) - { - sram_present = true; - memcpy( impl->sram, in.sram, min( (int) in.sram_size, (int) sizeof impl->sram ) ); - enable_sram( true ); // mapper can override (read-only, unmapped, etc.) - } - - if ( in.mapper_valid ) // restore last since it might reconfigure things - mapper->load_state( *in.mapper ); -} - -void Nes_Core::enable_prg_6000() -{ - sram_writable = 0; - sram_readable = 0; - lrom_readable = 0x8000; -} - -void Nes_Core::enable_sram( bool b, bool read_only ) -{ - sram_writable = 0; - if ( b ) - { - if ( !sram_present ) - { - sram_present = true; - memset( impl->sram, 0xFF, impl->sram_size ); - } - sram_readable = sram_end; - if ( !read_only ) - sram_writable = sram_end; - cpu::map_code( 0x6000, impl->sram_size, impl->sram ); - } - else - { - sram_readable = 0; - for ( int i = 0; i < impl->sram_size; i += cpu::page_size ) - cpu::map_code( 0x6000 + i, cpu::page_size, impl->unmapped_page ); - } -} - -// Unmapped memory - -#if !defined (NDEBUG) && 0 -static nes_addr_t last_unmapped_addr; -#endif - -void Nes_Core::log_unmapped( nes_addr_t addr, int data ) -{ - #if !defined (NDEBUG) && 0 - if ( last_unmapped_addr != addr ) - { - last_unmapped_addr = addr; - if ( data < 0 ) - dprintf( "Read unmapped %04X\n", addr ); - else - dprintf( "Write unmapped %04X <- %02X\n", addr, data ); - } - #endif -} - -inline void Nes_Core::cpu_adjust_time( int n ) -{ - ppu_2002_time -= n; - cpu_time_offset += n; - cpu::reduce_limit( n ); -} - -// I/O and sound - -int Nes_Core::read_dmc( void* data, nes_addr_t addr ) -{ - Nes_Core* emu = (Nes_Core*) data; - int result = *emu->cpu::get_code( addr ); - if ( wait_states_enabled ) - emu->cpu_adjust_time( 4 ); - return result; -} - -void Nes_Core::apu_irq_changed( void* emu ) -{ - ((Nes_Core*) emu)->irq_changed(); -} - -void Nes_Core::write_io( nes_addr_t addr, int data ) -{ - // sprite dma - if ( addr == 0x4014 ) - { - ppu.dma_sprites( clock(), cpu::get_code( data * 0x100 ) ); - cpu_adjust_time( 513 ); - return; - } - - // joypad strobe - if ( addr == 0x4016 ) - { - // if strobe goes low, latch data - if ( joypad.w4016 & 1 & ~data ) - { - joypad_read_count++; - joypad.joypad_latches [0] = current_joypad [0]; - joypad.joypad_latches [1] = current_joypad [1]; - } - joypad.w4016 = data; - return; - } - - // apu - if ( unsigned (addr - impl->apu.start_addr) <= impl->apu.end_addr - impl->apu.start_addr ) - { - impl->apu.write_register( clock(), addr, data ); - if ( wait_states_enabled ) - { - if ( addr == 0x4010 || (addr == 0x4015 && (data & 0x10)) ) - { - impl->apu.run_until( clock() + 1 ); - event_changed(); - } - } - return; - } - - #ifndef NDEBUG - log_unmapped( addr, data ); - #endif -} - -int Nes_Core::read_io( nes_addr_t addr ) -{ - if ( (addr & 0xFFFE) == 0x4016 ) - { - // to do: to aid with recording, doesn't emulate transparent latch, - // so a game that held strobe at 1 and read $4016 or $4017 would not get - // the current A status as occurs on a NES - int32_t result = joypad.joypad_latches [addr & 1]; - if ( !(joypad.w4016 & 1) ) - joypad.joypad_latches [addr & 1] = result >> 1; // ASR is intentional - return result & 1; - } - - if ( addr == Nes_Apu::status_addr ) - return impl->apu.read_status( clock() ); - - #ifndef NDEBUG - log_unmapped( addr ); - #endif - - return addr >> 8; // simulate open bus -} - -// CPU - -const int irq_inhibit_mask = 0x04; - -nes_addr_t Nes_Core::read_vector( nes_addr_t addr ) -{ - byte const* p = cpu::get_code( addr ); - return p [1] * 0x100 + p [0]; -} - -void Nes_Core::reset( bool full_reset, bool erase_battery_ram ) -{ - require( cart ); - - if ( full_reset ) - { - cpu::reset( impl->unmapped_page ); - cpu_time_offset = -1; - clock_ = 0; - - // Low RAM - memset( cpu::low_mem, 0xFF, low_ram_size ); - cpu::low_mem [8] = 0xf7; - cpu::low_mem [9] = 0xef; - cpu::low_mem [10] = 0xdf; - cpu::low_mem [15] = 0xbf; - - // SRAM - lrom_readable = 0; - sram_present = true; - enable_sram( false ); - if ( !cart->has_battery_ram() || erase_battery_ram ) - memset( impl->sram, 0xFF, impl->sram_size ); - - joypad.joypad_latches [0] = 0; - joypad.joypad_latches [1] = 0; - - nes.frame_count = 0; - } - - // to do: emulate partial reset - - ppu.reset( full_reset ); - impl->apu.reset(); - - mapper->reset(); - - cpu::r.pc = read_vector( 0xFFFC ); - cpu::r.sp = 0xfd; - cpu::r.a = 0; - cpu::r.x = 0; - cpu::r.y = 0; - cpu::r.status = irq_inhibit_mask; - nes.timestamp = 0; - error_count = 0; -} - -void Nes_Core::vector_interrupt( nes_addr_t vector ) -{ - cpu::push_byte( cpu::r.pc >> 8 ); - cpu::push_byte( cpu::r.pc & 0xFF ); - cpu::push_byte( cpu::r.status | 0x20 ); // reserved bit is set - - cpu_adjust_time( 7 ); - cpu::r.status |= irq_inhibit_mask; - cpu::r.pc = read_vector( vector ); -} - -inline nes_time_t Nes_Core::earliest_irq( nes_time_t present ) -{ - return min( impl->apu.earliest_irq( present ), mapper->next_irq( present ) ); -} - -void Nes_Core::irq_changed() -{ - cpu_set_irq_time( earliest_irq( cpu_time() ) ); -} - -inline nes_time_t Nes_Core::ppu_frame_length( nes_time_t present ) -{ - nes_time_t t = ppu.frame_length(); - if ( t > present ) - return t; - - ppu.render_bg_until( clock() ); // to do: why this call to clock() rather than using present? - return ppu.frame_length(); -} - -inline nes_time_t Nes_Core::earliest_event( nes_time_t present ) -{ - // PPU frame - nes_time_t t = ppu_frame_length( present ); - - // DMC - if ( wait_states_enabled ) - t = min( t, impl->apu.next_dmc_read_time() + 1 ); - - // NMI - t = min( t, ppu.nmi_time() ); - - if ( single_instruction_mode ) - t = min( t, present + 1 ); - - return t; -} - -void Nes_Core::event_changed() -{ - cpu_set_end_time( earliest_event( cpu_time() ) ); -} - -#undef NES_EMU_CPU_HOOK -#ifndef NES_EMU_CPU_HOOK - #define NES_EMU_CPU_HOOK( cpu, end_time ) cpu::run( end_time ) -#endif - -nes_time_t Nes_Core::emulate_frame_() -{ - Nes_Cpu::result_t last_result = cpu::result_cycles; - int extra_instructions = 0; - while ( true ) - { - // Add DMC wait-states to CPU time - if ( wait_states_enabled ) - { - impl->apu.run_until( cpu_time() ); - clock_ = cpu_time_offset; - } - - nes_time_t present = cpu_time(); - if ( present >= ppu_frame_length( present ) ) - { - if ( ppu.nmi_time() <= present ) - { - // NMI will occur next, so delayed CLI and SEI don't need to be handled. - // If NMI will occur normally ($2000.7 and $2002.7 set), let it occur - // next frame, otherwise vector it now. - - if ( !(ppu.w2000 & 0x80 & ppu.r2002) ) - { - dprintf( "vectored NMI at end of frame\n" ); - vector_interrupt( 0xFFFA ); - present += 7; - } - return present; - } - - if ( extra_instructions > 2 ) - { - check( last_result != cpu::result_sei && last_result != cpu::result_cli ); - check( ppu.nmi_time() >= 0x10000 || (ppu.w2000 & 0x80 & ppu.r2002) ); - return present; - } - - if ( last_result != cpu::result_cli && last_result != cpu::result_sei && - (ppu.nmi_time() >= 0x10000 || (ppu.w2000 & 0x80 & ppu.r2002)) ) - return present; - - dprintf( "Executing extra instructions for frame\n" ); - extra_instructions++; // execute one more instruction - } - - // NMI - if ( present >= ppu.nmi_time() ) - { - ppu.acknowledge_nmi(); - vector_interrupt( 0xFFFA ); - last_result = cpu::result_cycles; // most recent sei/cli won't be delayed now - } - - // IRQ - nes_time_t irq_time = earliest_irq( present ); - cpu_set_irq_time( irq_time ); - if ( present >= irq_time && (!(cpu::r.status & irq_inhibit_mask) || - last_result == cpu::result_sei) ) - { - if ( last_result != cpu::result_cli ) - { - //dprintf( "%6d IRQ vectored\n", present ); - mapper->run_until( present ); - vector_interrupt( 0xFFFE ); - } - else - { - // CLI delays IRQ - cpu_set_irq_time( present + 1 ); - check( false ); // rare event - } - } - - // CPU - nes_time_t end_time = earliest_event( present ); - if ( extra_instructions ) - end_time = present + 1; - unsigned long cpu_error_count = cpu::error_count(); - last_result = NES_EMU_CPU_HOOK( cpu, end_time - cpu_time_offset - 1 ); - cpu_adjust_time( cpu::time() ); - clock_ = cpu_time_offset; - error_count += cpu::error_count() - cpu_error_count; - } -} - -nes_time_t Nes_Core::emulate_frame() -{ - require( cart ); - - joypad_read_count = 0; - - cpu_time_offset = ppu.begin_frame( nes.timestamp ) - 1; - ppu_2002_time = 0; - clock_ = cpu_time_offset; - - check( cpu_time() == (int) nes.timestamp / ppu_overclock ); - check( 1 && impl->apu.last_time == cpu_time() ); - - // TODO: clean this fucking mess up - impl->apu.run_until_( emulate_frame_() ); - clock_ = cpu_time_offset; - impl->apu.run_until_( cpu_time() ); - check( 2 && clock_ == cpu_time_offset ); - check( 3 && impl->apu.last_time == cpu_time() ); - - nes_time_t ppu_frame_length = ppu.frame_length(); - nes_time_t length = cpu_time(); - nes.timestamp = ppu.end_frame( length ); - mapper->end_frame( length ); - impl->apu.end_frame( ppu_frame_length ); - check( 4 && cpu_time() == length ); - - check( 5 && impl->apu.last_time == length - ppu_frame_length ); - - disable_rendering(); - nes.frame_count++; - - return ppu_frame_length; -} - -void Nes_Core::add_mapper_intercept( nes_addr_t addr, unsigned size, bool read, bool write ) -{ - require( addr >= 0x4000 ); - require( addr + size <= 0x10000 ); - int end = (addr + size + (page_size - 1)) >> page_bits; - for ( int page = addr >> page_bits; page < end; page++ ) - { - data_reader_mapped [page] |= read; - data_writer_mapped [page] |= write; - } -} - diff --git a/quicknes/nes_emu/Nes_Core.h b/quicknes/nes_emu/Nes_Core.h deleted file mode 100644 index 179f9ef779..0000000000 --- a/quicknes/nes_emu/Nes_Core.h +++ /dev/null @@ -1,117 +0,0 @@ - -// Internal NES emulator - -// Nes_Emu 0.7.0 - -#ifndef NES_CORE_H -#define NES_CORE_H - -#include "blargg_common.h" -#include "Nes_Apu.h" -#include "Nes_Cpu.h" -#include "Nes_Ppu.h" -class Nes_Mapper; -class Nes_Cart; -class Nes_State; - -class Nes_Core : private Nes_Cpu { - typedef Nes_Cpu cpu; -public: - Nes_Core(); - ~Nes_Core(); - - blargg_err_t init(); - blargg_err_t open( Nes_Cart const* ); - void reset( bool full_reset = true, bool erase_battery_ram = false ); - blip_time_t emulate_frame(); - void close(); - - void save_state( Nes_State* ) const; - void save_state( Nes_State_* ) const; - void load_state( Nes_State_ const& ); - - void irq_changed(); - void event_changed(); - -public: private: friend class Nes_Emu; - - struct impl_t - { - enum { sram_size = 0x2000 }; - BOOST::uint8_t sram [sram_size]; - Nes_Apu apu; - - // extra byte allows CPU to always read operand of instruction, which - // might go past end of data - BOOST::uint8_t unmapped_page [::Nes_Cpu::page_size + 1]; - }; - impl_t* impl; // keep large arrays separate - unsigned long error_count; - bool sram_present; - -public: - uint32_t current_joypad [2]; - int joypad_read_count; - Nes_Cart const* cart; - Nes_Mapper* mapper; - nes_state_t nes; - Nes_Ppu ppu; - -private: - // noncopyable - Nes_Core( const Nes_Core& ); - Nes_Core& operator = ( const Nes_Core& ); - - // Timing - nes_time_t ppu_2002_time; - void disable_rendering() { clock_ = 0; } - nes_time_t earliest_irq( nes_time_t present ); - nes_time_t ppu_frame_length( nes_time_t present ); - nes_time_t earliest_event( nes_time_t present ); - - // APU and Joypad - joypad_state_t joypad; - int read_io( nes_addr_t ); - void write_io( nes_addr_t, int data ); - static int read_dmc( void* emu, nes_addr_t ); - static void apu_irq_changed( void* emu ); - - // CPU - unsigned sram_readable; - unsigned sram_writable; - unsigned lrom_readable; - nes_time_t clock_; - nes_time_t cpu_time_offset; - nes_time_t emulate_frame_(); - nes_addr_t read_vector( nes_addr_t ); - void vector_interrupt( nes_addr_t ); - static void log_unmapped( nes_addr_t addr, int data = -1 ); - void cpu_set_irq_time( nes_time_t t ) { cpu::set_irq_time_( t - 1 - cpu_time_offset ); } - void cpu_set_end_time( nes_time_t t ) { cpu::set_end_time_( t - 1 - cpu_time_offset ); } - nes_time_t cpu_time() const { return clock_ + 1; } - void cpu_adjust_time( int offset ); - -public: private: friend class Nes_Ppu; - void set_ppu_2002_time( nes_time_t t ) { ppu_2002_time = t - 1 - cpu_time_offset; } - -public: private: friend class Nes_Mapper; - void enable_prg_6000(); - void enable_sram( bool enabled, bool read_only = false ); - nes_time_t clock() const { return clock_; } - void add_mapper_intercept( nes_addr_t start, unsigned size, bool read, bool write ); - -public: private: friend class Nes_Cpu; - int cpu_read_ppu( nes_addr_t, nes_time_t ); - int cpu_read( nes_addr_t, nes_time_t ); - void cpu_write( nes_addr_t, int data, nes_time_t ); - void cpu_write_2007( int data ); - -private: - unsigned char data_reader_mapped [page_count + 1]; // extra entry for overflow - unsigned char data_writer_mapped [page_count + 1]; -}; - -int mem_differs( void const* p, int cmp, unsigned long s ); - -#endif - diff --git a/quicknes/nes_emu/Nes_Cpu.cpp b/quicknes/nes_emu/Nes_Cpu.cpp deleted file mode 100644 index 2a2188e18a..0000000000 --- a/quicknes/nes_emu/Nes_Cpu.cpp +++ /dev/null @@ -1,1268 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/nes-emu/ - -// TODO: remove -#if !defined (NDEBUG) && 0 - #pragma peephole on - #pragma global_optimizer on - #pragma optimization_level 4 - #pragma scheduling 604 - #undef BLARGG_ENABLE_OPTIMIZER -#endif - -#include "Nes_Cpu.h" - -#include -#include -#include "blargg_endian.h" - -#include "nes_cpu_io.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -inline void Nes_Cpu::set_code_page( int i, uint8_t const* p ) -{ - code_map [i] = p - (unsigned) i * page_size; -} - -void Nes_Cpu::reset( void const* unmapped_page ) -{ - r.status = 0; - r.sp = 0; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - - error_count_ = 0; - clock_count = 0; - clock_limit = 0; - irq_time_ = LONG_MAX / 2 + 1; - end_time_ = LONG_MAX / 2 + 1; - - assert( page_size == 0x800 ); // assumes this - set_code_page( 0, low_mem ); - set_code_page( 1, low_mem ); - set_code_page( 2, low_mem ); - set_code_page( 3, low_mem ); - for ( int i = 4; i < page_count + 1; i++ ) - set_code_page( i, (uint8_t*) unmapped_page ); - - #ifndef NDEBUG - blargg_verify_byte_order(); - #endif -} - -void Nes_Cpu::map_code( nes_addr_t start, unsigned size, const void* data ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - set_code_page( first_page + i, (uint8_t*) data + i * page_size ); -} - -// Note: 'addr' is evaulated more than once in the following macros, so it -// must not contain side-effects. - -//static void log_read( int opcode ) { LOG_FREQ( "read", 256, opcode ); } - -#define READ_LIKELY_PPU( addr ) (NES_CPU_READ_PPU( this, (addr), (clock_count) )) -#define READ( addr ) (NES_CPU_READ( this, (addr), (clock_count) )) -#define WRITE( addr, data ) {NES_CPU_WRITE( this, (addr), (data), (clock_count) );} - -#define READ_LOW( addr ) (low_mem [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) - -#define READ_PROG( addr ) (code_map [(addr) >> page_bits] [addr]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -int Nes_Cpu::read( nes_addr_t addr ) -{ - return READ( addr ); -} - -void Nes_Cpu::write( nes_addr_t addr, int value ) -{ - WRITE( addr, value ); -} - -void Nes_Cpu::set_tracecb(void (*cb)(unsigned int *data)) -{ - tracecb = cb; -} - -#ifndef NES_CPU_GLUE_ONLY - -static const unsigned char clock_table [256] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 - 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 - 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 - 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 - 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A - 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F -}; - -Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end ) -{ - set_end_time_( end ); - clock_count = 0; - - volatile result_t result = result_cycles; - -#if !BLARGG_CPU_CISC - long clock_count = this->clock_count; - uint8_t* const low_mem = this->low_mem; -#endif - - // registers - unsigned pc = r.pc; - int sp; - SET_SP( r.sp ); - int a = r.a; - int x = r.x; - int y = r.y; - - // status flags - - int const st_n = 0x80; - int const st_v = 0x40; - int const st_r = 0x20; - int const st_b = 0x10; - int const st_d = 0x08; - int const st_i = 0x04; - int const st_z = 0x02; - int const st_c = 0x01; - - #define IS_NEG (nz & 0x880) - - #define CALC_STATUS( out ) do { \ - out = status & (st_v | st_d | st_i); \ - out |= (c >> 8) & st_c; \ - if ( IS_NEG ) out |= st_n; \ - if ( !(nz & 0xFF) ) out |= st_z; \ - } while ( 0 ) - - #define SET_STATUS( in ) do { \ - status = in & (st_v | st_d | st_i); \ - c = in << 8; \ - nz = (in << 4) & 0x800; \ - nz |= ~in & st_z; \ - } while ( 0 ) - - int status; - int c; // carry set if (c & 0x100) != 0 - int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0 - { - int temp = r.status; - SET_STATUS( temp ); - } - - goto loop; -dec_clock_loop: - clock_count--; -loop: - - assert( (unsigned) GET_SP() < 0x100 ); - assert( (unsigned) a < 0x100 ); - assert( (unsigned) x < 0x100 ); - assert( (unsigned) y < 0x100 ); - - uint8_t const* page = code_map [pc >> page_bits]; - unsigned opcode = page [pc]; - pc++; - - if ( clock_count >= clock_limit ) - goto stop; - - if (tracecb) - { - unsigned int scratch[7]; - scratch[0] = a; - scratch[1] = x; - scratch[2] = y; - scratch[3] = sp; - scratch[4] = pc - 1; - scratch[5] = status; - scratch[6] = opcode; - tracecb(scratch); - } - - clock_count += clock_table [opcode]; - unsigned data; - data = page [pc]; - - switch ( opcode ) - { - -// Macros - -#define GET_OPERAND( addr ) page [addr] -#define GET_OPERAND16( addr ) GET_LE16( &page [addr] ) - -//#define GET_OPERAND( addr ) READ_PROG( addr ) -//#define GET_OPERAND16( addr ) READ_PROG16( addr ) - -#define ADD_PAGE (pc++, data += 0x100 * GET_OPERAND( pc )); -#define GET_ADDR() GET_OPERAND16( pc ) - -#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8; - -#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - -#define IND_Y(r,c) { \ - int temp = READ_LOW( data ) + y; \ - data = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \ - if (c) HANDLE_PAGE_CROSSING( temp ); \ - if (!(r) || (temp & 0x100)) \ - READ( data - ( temp & 0x100 ) ); \ - } - -#define IND_X { \ - int temp = data + x; \ - data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \ - } - -#define ARITH_ADDR_MODES( op ) \ -case op - 0x04: /* (ind,x) */ \ - IND_X \ - goto ptr##op; \ -case op + 0x0C: /* (ind),y */ \ - IND_Y(true,true) \ - goto ptr##op; \ -case op + 0x10: /* zp,X */ \ - data = uint8_t (data + x); \ -case op + 0x00: /* zp */ \ - data = READ_LOW( data ); \ - goto imm##op; \ -case op + 0x14: /* abs,Y */ \ - data += y; \ - goto ind##op; \ -case op + 0x18: /* abs,X */ \ - data += x; \ -ind##op: { \ - HANDLE_PAGE_CROSSING( data ); \ - int temp = data; \ - ADD_PAGE \ - if ( temp & 0x100 ) \ - READ( data - 0x100 ); \ - goto ptr##op; \ -} \ -case op + 0x08: /* abs */ \ - ADD_PAGE \ -ptr##op: \ - data = READ( data ); \ -case op + 0x04: /* imm */ \ -imm##op: \ - -#define ARITH_ADDR_MODES_PTR( op ) \ -case op - 0x04: /* (ind,x) */ \ - IND_X \ - goto imm##op; \ -case op + 0x0C: \ - IND_Y(false,false) \ - goto imm##op; \ -case op + 0x10: /* zp,X */ \ - data = uint8_t (data + x); \ - goto imm##op; \ -case op + 0x14: /* abs,Y */ \ - data += y; \ - goto ind##op; \ -case op + 0x18: /* abs,X */ \ - data += x; \ -ind##op: { \ - int temp = data; \ - ADD_PAGE \ - READ( data - ( temp & 0x100 ) ); \ - goto imm##op; \ -} \ -case op + 0x08: /* abs */ \ - ADD_PAGE \ -case op + 0x00: /* zp */ \ -imm##op: \ - -#define BRANCH( cond ) \ -{ \ - pc++; \ - int offset = (BOOST::int8_t) data; \ - int extra_clock = (pc & 0xFF) + offset; \ - if ( !(cond) ) goto dec_clock_loop; \ - pc += offset; \ - pc = BOOST::uint16_t( pc ); \ - clock_count += (extra_clock >> 8) & 1; \ - goto loop; \ -} - -// Often-Used - - case 0xB5: // LDA zp,x - data = uint8_t (data + x); - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - int temp = pc + 1; - pc = GET_OPERAND16( pc ); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x4C: // JMP abs - pc = GET_OPERAND16( pc ); - goto loop; - - case 0xE8: INC_DEC_XY( x, 1 ) // INX - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ); - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xC8: INC_DEC_XY( y, 1 ) // INY - - case 0xA8: // TAY - y = a; - case 0x98: // TYA - a = nz = y; - goto loop; - - case 0xAD:{// LDA abs - unsigned addr = GET_ADDR(); - pc += 2; - a = nz = READ_LIKELY_PPU( addr ); - goto loop; - } - - case 0x60: // RTS - pc = 1 + READ_LOW( sp ); - pc += READ_LOW( 0x100 | (sp - 0xFF) ) * 0x100; - sp = (sp - 0xFE) | 0x100; - goto loop; - - case 0x99: // STA abs,Y - data += y; - goto sta_ind_common; - - case 0x9D: // STA abs,X - data += x; - sta_ind_common: { - int temp = data; - ADD_PAGE - READ( data - ( temp & 0x100 ) ); - goto sta_ptr; - } - case 0x8D: // STA abs - ADD_PAGE - sta_ptr: - pc++; - WRITE( data, a ); - goto loop; - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - -#if 0 - case 0xA1: // LDA (ind,X) - IND_X - goto lda_ptr; - - case 0xB1: // LDA (ind),Y - IND_Y(true,true) - goto lda_ptr; - - case 0xB9: // LDA abs,Y - data += y; - goto lda_ind_common; - - case 0xBD: // LDA abs,X - data += x; - lda_ind_common: { - HANDLE_PAGE_CROSSING( data ); - int temp = data; - ADD_PAGE - if ( temp & 0x100 ) - READ( data - 0x100 ); - } - lda_ptr: - a = nz = READ( data ); - pc++; - goto loop; -#else - // optimization of most commonly used memory read instructions - - case 0xB9:// LDA abs,Y - data += y; - data -= x; - case 0xBD:{// LDA abs,X - pc++; - unsigned msb = GET_OPERAND( pc ); - data += x; - // indexed common - pc++; - HANDLE_PAGE_CROSSING( data ); - int temp = data; - data += msb * 0x100; - a = nz = READ_PROG( BOOST::uint16_t( data ) ); - if ( (unsigned) (data - 0x2000) >= 0x6000 ) - goto loop; - if ( temp & 0x100 ) - READ( data - 0x100 ); - a = nz = READ( data ); - goto loop; - } - - case 0xB1:{// LDA (ind),Y - unsigned msb = READ_LOW( (uint8_t) (data + 1) ); - data = READ_LOW( data ) + y; - // indexed common - pc++; - HANDLE_PAGE_CROSSING( data ); - int temp = data; - data += msb * 0x100; - a = nz = READ_PROG( BOOST::uint16_t( data ) ); - if ( (unsigned) (data - 0x2000) >= 0x6000 ) - goto loop; - if ( temp & 0x100 ) - READ( data - 0x100 ); - a = nz = READ( data ); - goto loop; - } - - case 0xA1: // LDA (ind,X) - IND_X - a = nz = READ( data ); - pc++; - goto loop; - -#endif - -// Branch - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - -// Load/store - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (data + y); - case 0xA6: // LDX zp - data = READ_LOW( data ); - case 0xA2: // LDX #imm - pc++; - x = data; - nz = data; - goto loop; - - case 0xB4: // LDY zp,x - data = uint8_t (data + x); - case 0xA4: // LDY zp - data = READ_LOW( data ); - case 0xA0: // LDY #imm - pc++; - y = data; - nz = data; - goto loop; - - case 0x91: // STA (ind),Y - IND_Y(false,false) - goto sta_ptr; - - case 0x81: // STA (ind,X) - IND_X - goto sta_ptr; - - case 0xBC: // LDY abs,X - data += x; - HANDLE_PAGE_CROSSING( data ); - case 0xAC:{// LDY abs - pc++; - unsigned addr = data + 0x100 * GET_OPERAND( pc ); - if ( data & 0x100 ) - READ( addr - 0x100 ); - pc++; - y = nz = READ( addr ); - goto loop; - } - - case 0xBE: // LDX abs,y - data += y; - HANDLE_PAGE_CROSSING( data ); - case 0xAE:{// LDX abs - pc++; - unsigned addr = data + 0x100 * GET_OPERAND( pc ); - pc++; - if ( data & 0x100 ) - READ( addr - 0x100 ); - x = nz = READ( addr ); - goto loop; - } - - { - int temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - unsigned addr = GET_ADDR(); - WRITE( addr, temp ); - pc += 2; - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - unsigned addr = GET_ADDR(); - pc++; - data = READ( addr ); - goto cpx_data; - } - - case 0xE4: // CPX zp - data = READ_LOW( data ); - case 0xE0: // CPX #imm - cpx_data: - nz = x - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - unsigned addr = GET_ADDR(); - pc++; - data = READ( addr ); - goto cpy_data; - } - - case 0xC4: // CPY zp - data = READ_LOW( data ); - case 0xC0: // CPY #imm - cpy_data: - nz = y - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - -// Logical - - ARITH_ADDR_MODES( 0x25 ) // AND - nz = (a &= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x45 ) // EOR - nz = (a ^= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x05 ) // ORA - nz = (a |= data); - pc++; - goto loop; - - case 0x2C:{// BIT abs - unsigned addr = GET_ADDR(); - pc += 2; - status &= ~st_v; - nz = READ_LIKELY_PPU( addr ); - status |= nz & st_v; - if ( a & nz ) - goto loop; - // result must be zero, even if N bit is set - nz = nz << 4 & 0x800; - goto loop; - } - - case 0x24: // BIT zp - nz = READ_LOW( data ); - pc++; - status &= ~st_v; - status |= nz & st_v; - if ( a & nz ) - goto loop; - // result must be zero, even if N bit is set - nz = nz << 4 & 0x800; - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - case 0xEB: // unofficial equivalent - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - int carry = (c >> 8) & 1; - int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= (ov >> 2) & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) nz; - goto loop; - } - -// Shift/rotate - - case 0x4A: // LSR A - lsr_a: - c = 0; - case 0x6A: // ROR A - nz = (c >> 1) & 0x80; // could use bit insert macro here - c = a << 8; - nz |= a >> 1; - a = nz; - goto loop; - - case 0x0A: // ASL A - nz = a << 1; - c = nz; - a = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - int temp = (c >> 8) & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x3E: // ROL abs,X - data += x; - goto rol_abs; - - case 0x1E: // ASL abs,X - data += x; - case 0x0E: // ASL abs - c = 0; - case 0x2E: // ROL abs - rol_abs: { - int temp = data; - ADD_PAGE - if ( opcode == 0x1E || opcode == 0x3E ) READ( data - ( temp & 0x100 ) ); - WRITE( data, temp = READ( data ) ); - nz = (c >> 8) & 1; - nz |= (c = temp << 1); - } - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - int temp = data; - ADD_PAGE - if ( opcode == 0x5E || opcode == 0x7E ) READ( data - ( temp & 0x100 ) ); - WRITE( data, temp = READ( data ) ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - goto rotate_common; - } - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (data + x); - case 0x46: // LSR zp - c = 0; - case 0x66: // ROR zp - ror_zp: { - int temp = READ_LOW( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - goto write_nz_zp; - } - - case 0x36: // ROL zp,x - data = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (data + x); - case 0x06: // ASL zp - c = 0; - case 0x26: // ROL zp - rol_zp: - nz = (c >> 8) & 1; - nz |= (c = READ_LOW( data ) << 1); - goto write_nz_zp; - -// Increment/decrement - - case 0xCA: INC_DEC_XY( x, -1 ) // DEX - - case 0x88: INC_DEC_XY( y, -1 ) // DEY - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = -1; - add_nz_zp: - nz += READ_LOW( data ); - write_nz_zp: - pc++; - WRITE_LOW( data, nz ); - goto loop; - - case 0xFE: { // INC abs,x - int temp = data + x; - data = x + GET_ADDR(); - READ( data - ( temp & 0x100 ) ); - goto inc_ptr; - } - - case 0xEE: // INC abs - data = GET_ADDR(); - inc_ptr: - nz = 1; - goto inc_common; - - case 0xDE: { // DEC abs,x - int temp = data + x; - data = x + GET_ADDR(); - READ( data - ( temp & 0x100 ) ); - goto dec_ptr; - } - - case 0xCE: // DEC abs - data = GET_ADDR(); - dec_ptr: - nz = -1; - inc_common: { - int temp; - WRITE( data, temp = READ( data ) ); - nz += temp; - pc += 2; - WRITE( data, (uint8_t) nz ); - goto loop; - } - -// Transfer - - case 0xAA: // TAX - x = a; - case 0x8A: // TXA - a = nz = x; - goto loop; - - case 0x9A: // TXS - SET_SP( x ); // verified (no flag change) - goto loop; - - case 0xBA: // TSX - x = nz = GET_SP(); - goto loop; - -// Stack - - case 0x48: // PHA - PUSH( a ); // verified - goto loop; - - case 0x68: // PLA - a = nz = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - goto loop; - - case 0x40: // RTI - { - int temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - } - if ( !((data ^ status) & st_i) ) - goto loop; // I flag didn't change - i_flag_changed: - //dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") ); - this->r.status = status; // update externally-visible I flag - // update clock_limit based on modified I flag - clock_limit = end_time_; - if ( end_time_ <= irq_time_ ) - goto loop; - if ( status & st_i ) - goto loop; - clock_limit = irq_time_; - goto loop; - - case 0x28:{// PLP - int temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - data = status; - SET_STATUS( temp ); - if ( !((data ^ status) & st_i) ) - goto loop; // I flag didn't change - if ( !(status & st_i) ) - goto handle_cli; - goto handle_sei; - } - - case 0x08: { // PHP - int temp; - CALC_STATUS( temp ); - PUSH( temp | st_b | st_r ); - goto loop; - } - - case 0x6C: // JMP (ind) - data = GET_ADDR(); - pc = READ( data ); - pc |= READ( (data & 0xFF00) | ((data + 1) & 0xFF) ) << 8; - goto loop; - - case 0x00: { // BRK - pc++; - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - int temp; - CALC_STATUS( temp ); - sp = (sp - 3) | 0x100; - WRITE_LOW( sp, temp | st_b | st_r ); - pc = GET_LE16( &code_map [0xFFFE >> page_bits] [0xFFFE] ); - status |= st_i; - goto i_flag_changed; - } - -// Flags - - case 0x38: // SEC - c = ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: - //dprintf( "%6d CLI\n", time() ); - this->r.status = status; // update externally-visible I flag - if ( clock_count < end_time_ ) - { - assert( clock_limit == end_time_ ); - if ( end_time_ <= irq_time_ ) - goto loop; // irq is later - if ( clock_count >= irq_time_ ) - irq_time_ = clock_count + 1; // delay IRQ until after next instruction - clock_limit = irq_time_; - goto loop; - } - // execution is stopping now, so delayed CLI must be handled by caller - result = result_cli; - goto end; - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: - //dprintf( "%6d SEI\n", time() ); - this->r.status = status; // update externally-visible I flag - clock_limit = end_time_; - if ( clock_count < irq_time_ ) - goto loop; - result = result_sei; // IRQ will occur now, even though I flag is set - goto end; - -// Unofficial - case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: { // SKW - data += x; - HANDLE_PAGE_CROSSING( data ); - int addr = GET_ADDR() + x; - if ( data & 0x100 ) - READ( addr - 0x100 ); - READ( addr ); - } - case 0x0C: // SKW - pc++; - case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB - case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: - pc++; - case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP - goto loop; - - ARITH_ADDR_MODES_PTR( 0xC7 ) // DCP - WRITE( data, nz = READ( data ) ); - nz = uint8_t( nz - 1 ); - WRITE( data, nz ); - pc++; - nz = a - nz; - c = ~nz; - nz &= 0xFF; - goto loop; - - ARITH_ADDR_MODES_PTR( 0xE7 ) // ISC - WRITE( data, nz = READ( data ) ); - nz = uint8_t( nz + 1 ); - WRITE( data, nz ); - data = nz ^ 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES_PTR( 0x27 ) { // RLA - WRITE( data, nz = READ( data ) ); - int temp = c; - c = nz << 1; - nz = uint8_t( c ) | ( ( temp >> 8 ) & 0x01 ); - WRITE( data, nz ); - pc++; - nz = a &= nz; - goto loop; - } - - ARITH_ADDR_MODES_PTR( 0x67 ) { // RRA - int temp; - WRITE( data, temp = READ( data ) ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - WRITE( data, nz ); - data = nz; - c = temp << 8; - goto adc_imm; - } - - ARITH_ADDR_MODES_PTR( 0x07 ) // SLO - WRITE( data, nz = READ( data ) ); - c = nz << 1; - nz = uint8_t( c ); - WRITE( data, nz ); - nz = (a |= nz); - pc++; - goto loop; - - ARITH_ADDR_MODES_PTR( 0x47 ) // SRE - WRITE( data, nz = READ( data ) ); - c = nz << 8; - nz >>= 1; - WRITE( data, nz ); - nz = a ^= nz; - pc++; - goto loop; - - case 0x4B: // ALR - nz = (a &= data); - pc++; - goto lsr_a; - - case 0x0B: // ANC - case 0x2B: - nz = a &= data; - c = a << 1; - pc++; - goto loop; - - case 0x6B: // ARR - nz = a = uint8_t( ( ( data & a ) >> 1 ) | ( ( c >> 1 ) & 0x80 ) ); - c = a << 2; - status = ( status & ~st_v ) | ( ( a ^ a << 1 ) & st_v ); - pc++; - goto loop; - - case 0xAB: // LXA - a = data; - x = data; - nz = data; - pc++; - goto loop; - - case 0xA3: // LAX - IND_X - goto lax_ptr; - - case 0xB3: - IND_Y(true,true) - goto lax_ptr; - - case 0xB7: - data = uint8_t (data + y); - - case 0xA7: - data = READ_LOW( data ); - goto lax_imm; - - case 0xBF: { - data += y; - HANDLE_PAGE_CROSSING( data ); - int temp = data; - ADD_PAGE; - if ( temp & 0x100 ) - READ( data - 0x100 ); - goto lax_ptr; - } - - case 0xAF: - ADD_PAGE - - lax_ptr: - data = READ( data ); - lax_imm: - nz = x = a = data; - pc++; - goto loop; - - case 0x83: // SAX - IND_X - goto sax_imm; - - case 0x97: - data = uint8_t (data + y); - goto sax_imm; - - case 0x8F: - ADD_PAGE - - case 0x87: - sax_imm: - WRITE( data, a & x ); - pc++; - goto loop; - - case 0xCB: // SBX - data = ( a & x ) - data; - c = ( data <= 0xFF ) ? 0x100 : 0; - nz = x = uint8_t( data ); - pc++; - goto loop; - - case 0x93: // SHA (ind),Y - IND_Y(false,false) - pc++; - WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) ); - goto loop; - - case 0x9F: { // SHA abs,Y - data += y; - int temp = data; - ADD_PAGE - READ( data - ( temp & 0x100 ) ); - pc++; - WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) ); - goto loop; - } - - case 0x9E: { // SHX abs,Y - data += y; - int temp = data; - ADD_PAGE - READ( data - ( temp & 0x100 ) ); - pc++; - if ( !( temp & 0x100 ) ) - WRITE( data, uint8_t( x & ( ( data >> 8 ) + 1 ) ) ); - goto loop; - } - - case 0x9C: { // SHY abs,X - data += x; - int temp = data; - ADD_PAGE - READ( data - ( temp & 0x100 ) ); - pc++; - if ( !( temp & 0x100) ) - WRITE( data, uint8_t( y & ( ( data >> 8 ) + 1 ) ) ); - goto loop; - } - - case 0x9B: { // SHS abs,Y - data += y; - int temp = data; - ADD_PAGE - READ( data - ( temp & 0x100 ) ); - pc++; - SET_SP( a & x ); - WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) ); - goto loop; - } - - case 0xBB: { // LAS abs,Y - data += y; - HANDLE_PAGE_CROSSING( data ); - int temp = data; - ADD_PAGE - if ( temp & 0x100 ) - READ( data - 0x100 ); - pc++; - a = GET_SP(); - x = a &= READ( data ); - SET_SP( a ); - goto loop; - } - -// Unimplemented - - case page_wrap_opcode: // HLT - if ( pc > 0x10000 ) - { - // handle wrap-around (assumes caller has put page of HLT at 0x10000) - pc = (pc - 1) & 0xFFFF; - clock_count -= 2; - goto loop; - } - // fall through - default: - // skip over proper number of bytes - static unsigned char const row [8] = { 0x95, 0x95, 0x95, 0xd5, 0x95, 0x95, 0xd5, 0xf5 }; - int len = row [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; - if ( opcode == 0x9C ) - len = 3; - pc += len - 1; - error_count_++; - goto loop; - - //result = result_badop; // TODO: re-enable - goto stop; - } - - // If this fails then the case above is missing an opcode - assert( false ); - -stop: - pc--; -end: - - { - int temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->clock_count = clock_count; - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - irq_time_ = LONG_MAX / 2 + 1; - - return result; -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Cpu.h b/quicknes/nes_emu/Nes_Cpu.h deleted file mode 100644 index 44fcabe2ff..0000000000 --- a/quicknes/nes_emu/Nes_Cpu.h +++ /dev/null @@ -1,132 +0,0 @@ - -// NES 6502 CPU emulator - -// Nes_Emu 0.7.0 - -#ifndef NES_CPU_H -#define NES_CPU_H - -#include "blargg_common.h" - -typedef long nes_time_t; // clock cycle count -typedef unsigned nes_addr_t; // 16-bit address - -class Nes_Cpu { -public: - typedef BOOST::uint8_t uint8_t; - - // Clear registers, unmap memory, and map code pages to unmapped_page. - void reset( void const* unmapped_page = 0 ); - - // Map code memory (memory accessed via the program counter). Start and size - // must be multiple of page_size. - enum { page_bits = 11 }; - enum { page_count = 0x10000 >> page_bits }; - enum { page_size = 1L << page_bits }; - void map_code( nes_addr_t start, unsigned size, void const* code ); - - // Access memory as the emulated CPU does. - int read( nes_addr_t ); - void write( nes_addr_t, int data ); - uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code - - // Push a byte on the stack - void push_byte( int ); - - // NES 6502 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; - }; - //registers_t r; - - // Reasons that run() returns - enum result_t { - result_cycles, // Requested number of cycles (or more) were executed - result_sei, // I flag just set and IRQ time would generate IRQ now - result_cli, // I flag just cleared but IRQ should occur *after* next instr - result_badop // unimplemented/illegal instruction - }; - - result_t run( nes_time_t end_time ); - - nes_time_t time() const { return clock_count; } - void reduce_limit( int offset ); - void set_end_time_( nes_time_t t ); - void set_irq_time_( nes_time_t t ); - unsigned long error_count() const { return error_count_; } - - // If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped. - enum { page_wrap_opcode = 0xF2 }; - - // One of the many opcodes that are undefined and stop CPU emulation. - enum { bad_opcode = 0xD2 }; - - void set_tracecb(void (*cb)(unsigned int *dest)); - -private: - uint8_t const* code_map [page_count + 1]; - nes_time_t clock_limit; - nes_time_t clock_count; - nes_time_t irq_time_; - nes_time_t end_time_; - unsigned long error_count_; - - enum { irq_inhibit = 0x04 }; - void set_code_page( int, uint8_t const* ); - void update_clock_limit(); - - void (*tracecb)(unsigned int *dest); - -public: - registers_t r; - - // low_mem is a full page size so it can be mapped with code_map - uint8_t low_mem [page_size > 0x800 ? page_size : 0x800]; -}; - -inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr ) -{ - return (uint8_t*) code_map [addr >> page_bits] + addr; -} - -inline void Nes_Cpu::update_clock_limit() -{ - nes_time_t t = end_time_; - if ( t > irq_time_ && !(r.status & irq_inhibit) ) - t = irq_time_; - clock_limit = t; -} - -inline void Nes_Cpu::set_end_time_( nes_time_t t ) -{ - end_time_ = t; - update_clock_limit(); -} - -inline void Nes_Cpu::set_irq_time_( nes_time_t t ) -{ - irq_time_ = t; - update_clock_limit(); -} - -inline void Nes_Cpu::reduce_limit( int offset ) -{ - clock_limit -= offset; - end_time_ -= offset; - irq_time_ -= offset; -} - -inline void Nes_Cpu::push_byte( int data ) -{ - int sp = r.sp; - r.sp = (sp - 1) & 0xFF; - low_mem [0x100 + sp] = data; -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Effects_Buffer.cpp b/quicknes/nes_emu/Nes_Effects_Buffer.cpp deleted file mode 100644 index 27acb356e6..0000000000 --- a/quicknes/nes_emu/Nes_Effects_Buffer.cpp +++ /dev/null @@ -1,84 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/libs/ - -#include "Nes_Effects_Buffer.h" - -#include "Nes_Apu.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -Nes_Effects_Buffer::Nes_Effects_Buffer() : - Effects_Buffer( true ) // nes never uses stereo channels -{ - config_t c; - c.effects_enabled = false; - config( c ); -} - -Nes_Effects_Buffer::~Nes_Effects_Buffer() { } - -Multi_Buffer* set_apu( Nes_Effects_Buffer* buf, Nes_Apu* apu ) -{ - buf->set_apu( apu ); - return buf; -} - -void Nes_Effects_Buffer::enable_nonlinearity( bool b ) -{ - if ( b ) - clear(); - Nes_Apu* apu = nonlin.enable( b, channel( 2 ).center ); - apu->osc_output( 0, channel( 0 ).center ); - apu->osc_output( 1, channel( 1 ).center ); -} - -void Nes_Effects_Buffer::config( const config_t& in ) -{ - config_t c = in; - if ( !c.effects_enabled ) - { - // effects must always be enabled to keep separate buffers, so - // set parameters to be equivalent to disabled - c.pan_1 = 0; - c.pan_2 = 0; - c.echo_level = 0; - c.reverb_level = 0; - c.effects_enabled = true; - } - Effects_Buffer::config( c ); -} - -blargg_err_t Nes_Effects_Buffer::set_sample_rate( long rate, int msec ) -{ - enable_nonlinearity( nonlin.enabled ); // reapply - return Effects_Buffer::set_sample_rate( rate, msec ); -} - -void Nes_Effects_Buffer::clear() -{ - nonlin.clear(); - Effects_Buffer::clear(); -} - -Nes_Effects_Buffer::channel_t Nes_Effects_Buffer::channel( int i ) -{ - return Effects_Buffer::channel( (2 <= i && i <= 4) ? 2 : i & 1 ); -} - -long Nes_Effects_Buffer::read_samples( blip_sample_t* out, long count ) -{ - count = 2 * nonlin.make_nonlinear( *channel( 2 ).center, count / 2 ); - return Effects_Buffer::read_samples( out, count ); -} - diff --git a/quicknes/nes_emu/Nes_Effects_Buffer.h b/quicknes/nes_emu/Nes_Effects_Buffer.h deleted file mode 100644 index 1861114f1a..0000000000 --- a/quicknes/nes_emu/Nes_Effects_Buffer.h +++ /dev/null @@ -1,38 +0,0 @@ - -// Effects_Buffer with non-linear sound - -// Nes_Emu 0.7.0 - -#ifndef NES_EFFECTS_BUFFER_H -#define NES_EFFECTS_BUFFER_H - -#include "Nes_Buffer.h" -#include "Effects_Buffer.h" - -// Effects_Buffer uses several buffers and outputs stereo sample pairs. -class Nes_Effects_Buffer : public Effects_Buffer { -public: - Nes_Effects_Buffer(); - ~Nes_Effects_Buffer(); - - // Setup APU for use with buffer, including setting its output to this buffer. - // If you're using Nes_Emu, this is automatically called for you. - void set_apu( Nes_Apu* apu ) { nonlin.set_apu( apu ); } - - // Enable/disable non-linear output - void enable_nonlinearity( bool = true ); - - // See Effects_Buffer.h for reference - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void config( const config_t& ); - void clear(); - channel_t channel( int ); - long read_samples( blip_sample_t*, long ); - -private: - Nes_Nonlinearizer nonlin; - friend Multi_Buffer* set_apu( Nes_Effects_Buffer*, Nes_Apu* ); -}; - -#endif - diff --git a/quicknes/nes_emu/Nes_Emu.cpp b/quicknes/nes_emu/Nes_Emu.cpp deleted file mode 100644 index 6c8aa6ca9e..0000000000 --- a/quicknes/nes_emu/Nes_Emu.cpp +++ /dev/null @@ -1,506 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Emu.h" - -#include -#include "Nes_State.h" -#include "Nes_Mapper.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -int const sound_fade_size = 384; - -// Constants are manually duplicated in Nes_Emu so their value can be seen -// directly, rather than having to look in Nes_Ppu.h. "0 +" converts to int. -BOOST_STATIC_ASSERT( Nes_Emu::image_width == 0 + Nes_Ppu::image_width ); -BOOST_STATIC_ASSERT( Nes_Emu::image_height == 0 + Nes_Ppu::image_height ); - -Nes_Emu::equalizer_t const Nes_Emu::nes_eq = { -1.0, 80 }; -Nes_Emu::equalizer_t const Nes_Emu::famicom_eq = { -15.0, 80 }; -Nes_Emu::equalizer_t const Nes_Emu::tv_eq = { -12.0, 180 }; - -Nes_Emu::Nes_Emu() -{ - frame_ = &single_frame; - buffer_height_ = Nes_Ppu::buffer_height + 2; - default_sound_buf = NULL; - sound_buf = &silent_buffer; - sound_buf_changed_count = 0; - equalizer_ = nes_eq; - channel_count_ = 0; - sound_enabled = false; - host_pixels = NULL; - single_frame.pixels = 0; - single_frame.top = 0; - init_called = false; - set_palette_range( 0 ); - memset( single_frame.palette, 0, sizeof single_frame.palette ); - host_pixel_buff = new char[buffer_width * buffer_height()]; - set_pixels(host_pixel_buff, buffer_width); -} - -Nes_Emu::~Nes_Emu() -{ - delete default_sound_buf; - delete[] host_pixel_buff; -} - -blargg_err_t Nes_Emu::init_() -{ - return emu.init(); -} - -inline blargg_err_t Nes_Emu::auto_init() -{ - if ( !init_called ) - { - RETURN_ERR( init_() ); - init_called = true; - } - return 0; -} - -inline void Nes_Emu::clear_sound_buf() -{ - fade_sound_out = false; - fade_sound_in = true; - sound_buf->clear(); -} - -// Emulation - -void Nes_Emu::close() -{ - if ( cart() ) - { - emu.close(); - private_cart.clear(); - } -} - -blargg_err_t Nes_Emu::set_cart( Nes_Cart const* new_cart ) -{ - close(); - RETURN_ERR( auto_init() ); - RETURN_ERR( emu.open( new_cart ) ); - - channel_count_ = Nes_Apu::osc_count + emu.mapper->channel_count(); - RETURN_ERR( sound_buf->set_channel_count( channel_count() ) ); - set_equalizer( equalizer_ ); - enable_sound( true ); - - reset(); - - return 0; -} - -void Nes_Emu::reset( bool full_reset, bool erase_battery_ram ) -{ - require( cart() ); - - clear_sound_buf(); - set_timestamp( 0 ); - emu.reset( full_reset, erase_battery_ram ); -} - -void Nes_Emu::set_palette_range( int begin, int end ) -{ - require( (unsigned) end <= 0x100 ); - // round up to alignment - emu.ppu.palette_begin = (begin + palette_alignment - 1) & ~(palette_alignment - 1); - host_palette_size = end - emu.ppu.palette_begin; - require( host_palette_size >= palette_alignment ); -} - -blargg_err_t Nes_Emu::emulate_frame( const uint32_t joypad1, const uint32_t joypad2 ) -{ - emu.current_joypad [0] = joypad1; - emu.current_joypad [1] = joypad2; - - emu.ppu.host_pixels = NULL; - - unsigned changed_count = sound_buf->channels_changed_count(); - bool new_enabled = (frame_ != NULL); - if ( sound_buf_changed_count != changed_count || sound_enabled != new_enabled ) - { - sound_buf_changed_count = changed_count; - sound_enabled = new_enabled; - enable_sound( sound_enabled ); - } - - frame_t* f = frame_; - if ( f ) - { - emu.ppu.max_palette_size = host_palette_size; - emu.ppu.host_palette = f->palette + emu.ppu.palette_begin; - // add black and white for emulator to use (unless emulator uses entire - // palette for frame) - f->palette [252] = 0x0F; - f->palette [254] = 0x30; - f->palette [255] = 0x0F; - if ( host_pixels ) - emu.ppu.host_pixels = (BOOST::uint8_t*) host_pixels + - emu.ppu.host_row_bytes * f->top; - - if ( sound_buf->samples_avail() ) - clear_sound_buf(); - - nes_time_t frame_len = emu.emulate_frame(); - sound_buf->end_frame( frame_len, false ); - - f = frame_; - f->sample_count = sound_buf->samples_avail(); - f->chan_count = sound_buf->samples_per_frame(); - f->palette_begin = emu.ppu.palette_begin; - f->palette_size = emu.ppu.palette_size; - f->joypad_read_count = emu.joypad_read_count; - f->burst_phase = emu.ppu.burst_phase; - f->pitch = emu.ppu.host_row_bytes; - f->pixels = emu.ppu.host_pixels + f->left; - } - else - { - emu.ppu.max_palette_size = 0; - emu.emulate_frame(); - } - - return 0; -} - -// Extras - -blargg_err_t Nes_Emu::load_ines( Auto_File_Reader in ) -{ - close(); - RETURN_ERR( private_cart.load_ines( in ) ); - return set_cart( &private_cart ); -} - -blargg_err_t Nes_Emu::save_battery_ram( Auto_File_Writer out ) -{ - RETURN_ERR( out.open() ); - return out->write( emu.impl->sram, emu.impl->sram_size ); -} - -blargg_err_t Nes_Emu::load_battery_ram( Auto_File_Reader in ) -{ - RETURN_ERR( in.open() ); - emu.sram_present = true; - return in->read( emu.impl->sram, emu.impl->sram_size ); -} - -void Nes_Emu::load_state( Nes_State_ const& in ) -{ - clear_sound_buf(); - emu.load_state( in ); -} - -void Nes_Emu::load_state( Nes_State const& in ) -{ - loading_state( in ); - load_state( STATIC_CAST(Nes_State_ const&,in) ); -} - -blargg_err_t Nes_Emu::load_state( Auto_File_Reader in ) -{ - Nes_State* state = BLARGG_NEW Nes_State; - CHECK_ALLOC( state ); - blargg_err_t err = state->read( in ); - if ( !err ) - load_state( *state ); - delete state; - return err; -} - -blargg_err_t Nes_Emu::save_state( Auto_File_Writer out ) const -{ - Nes_State* state = BLARGG_NEW Nes_State; - CHECK_ALLOC( state ); - save_state( state ); - blargg_err_t err = state->write( out ); - delete state; - return err; -} - -void Nes_Emu::write_chr( void const* p, long count, long offset ) -{ - require( (unsigned long) offset <= (unsigned long) chr_size() ); - long end = offset + count; - require( (unsigned long) end <= (unsigned long) chr_size() ); - memcpy( (byte*) chr_mem() + offset, p, count ); - emu.ppu.rebuild_chr( offset, end ); -} - -blargg_err_t Nes_Emu::set_sample_rate( long rate, class Nes_Buffer* buf ) -{ - extern Multi_Buffer* set_apu( class Nes_Buffer*, Nes_Apu* ); - RETURN_ERR( auto_init() ); - return set_sample_rate( rate, set_apu( buf, &emu.impl->apu ) ); -} - -blargg_err_t Nes_Emu::set_sample_rate( long rate, class Nes_Effects_Buffer* buf ) -{ - extern Multi_Buffer* set_apu( class Nes_Effects_Buffer*, Nes_Apu* ); - RETURN_ERR( auto_init() ); - return set_sample_rate( rate, set_apu( buf, &emu.impl->apu ) ); -} - -// Sound - -void Nes_Emu::set_frame_rate( double rate ) -{ - sound_buf->clock_rate( (long) (1789773 / 60.0 * rate) ); -} - -blargg_err_t Nes_Emu::set_sample_rate( long rate, Multi_Buffer* new_buf ) -{ - require( new_buf ); - RETURN_ERR( auto_init() ); - emu.impl->apu.volume( 1.0 ); // cancel any previous non-linearity - RETURN_ERR( new_buf->set_sample_rate( rate, 1200 / frame_rate ) ); - sound_buf = new_buf; - sound_buf_changed_count = 0; - if ( new_buf != default_sound_buf ) - { - delete default_sound_buf; - default_sound_buf = NULL; - } - set_frame_rate( frame_rate ); - return 0; -} - -blargg_err_t Nes_Emu::set_sample_rate( long rate ) -{ - if ( !default_sound_buf ) - CHECK_ALLOC( default_sound_buf = BLARGG_NEW Mono_Buffer ); - return set_sample_rate( rate, default_sound_buf ); -} - -void Nes_Emu::set_equalizer( equalizer_t const& eq ) -{ - equalizer_ = eq; - if ( cart() ) - { - blip_eq_t blip_eq( eq.treble, 0, sound_buf->sample_rate() ); - emu.impl->apu.treble_eq( blip_eq ); - emu.mapper->set_treble( blip_eq ); - sound_buf->bass_freq( equalizer_.bass ); - } -} - -void Nes_Emu::enable_sound( bool enabled ) -{ - if ( enabled ) - { - for ( int i = channel_count(); i-- > 0; ) - { - Blip_Buffer* buf = sound_buf->channel( i ).center; - int mapper_index = i - Nes_Apu::osc_count; - if ( mapper_index < 0 ) - emu.impl->apu.osc_output( i, buf ); - else - emu.mapper->set_channel_buf( mapper_index, buf ); - } - } - else - { - emu.impl->apu.output( NULL ); - for ( int i = channel_count() - Nes_Apu::osc_count; i-- > 0; ) - emu.mapper->set_channel_buf( i, NULL ); - } -} - -void Nes_Emu::fade_samples( blip_sample_t* p, int size, int step ) -{ - if ( size >= sound_fade_size ) - { - if ( step < 0 ) - p += size - sound_fade_size; - - int const shift = 15; - int mul = (1 - step) << (shift - 1); - step *= (1 << shift) / sound_fade_size; - - for ( int n = sound_fade_size; n--; ) - { - *p = (*p * mul) >> 15; - ++p; - mul += step; - } - } -} - -long Nes_Emu::read_samples( short* out, long out_size ) -{ - require( out_size >= sound_buf->samples_avail() ); - long count = sound_buf->read_samples( out, out_size ); - if ( fade_sound_in ) - { - fade_sound_in = false; - fade_samples( out, count, 1 ); - } - - if ( fade_sound_out ) - { - fade_sound_out = false; - fade_sound_in = true; // next buffer should be faded in - fade_samples( out, count, -1 ); - } - return count; -} - -Nes_Emu::rgb_t const Nes_Emu::nes_colors [color_table_size] = -{ - // generated with nes_ntsc default settings - {102,102,102},{ 0, 42,136},{ 20, 18,168},{ 59, 0,164}, - { 92, 0,126},{110, 0, 64},{108, 7, 0},{ 87, 29, 0}, - { 52, 53, 0},{ 12, 73, 0},{ 0, 82, 0},{ 0, 79, 8}, - { 0, 64, 78},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {174,174,174},{ 21, 95,218},{ 66, 64,254},{118, 39,255}, - {161, 27,205},{184, 30,124},{181, 50, 32},{153, 79, 0}, - {108,110, 0},{ 56,135, 0},{ 13,148, 0},{ 0,144, 50}, - { 0,124,142},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {254,254,254},{100,176,254},{147,144,254},{199,119,254}, - {243,106,254},{254,110,205},{254,130,112},{235,159, 35}, - {189,191, 0},{137,217, 0},{ 93,229, 48},{ 69,225,130}, - { 72,206,223},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {254,254,254},{193,224,254},{212,211,254},{233,200,254}, - {251,195,254},{254,197,235},{254,205,198},{247,217,166}, - {229,230,149},{208,240,151},{190,245,171},{180,243,205}, - {181,236,243},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - - {114, 83, 79},{ 0, 23,113},{ 32, 0,145},{ 71, 0,141}, - {104, 0,103},{122, 0, 41},{120, 0, 0},{ 99, 10, 0}, - { 64, 34, 0},{ 24, 54, 0},{ 0, 63, 0},{ 0, 60, 0}, - { 0, 45, 54},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {190,148,143},{ 37, 69,187},{ 83, 38,228},{134, 13,224}, - {177, 1,174},{200, 4, 92},{198, 24, 1},{170, 53, 0}, - {124, 84, 0},{ 73,109, 0},{ 30,122, 0},{ 6,118, 19}, - { 9, 98,110},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {254,222,215},{122,142,254},{168,110,254},{220, 85,254}, - {254, 72,247},{254, 76,164},{254, 96, 71},{254,125, 0}, - {210,157, 0},{158,183, 0},{114,195, 7},{ 90,191, 89}, - { 93,172,182},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {254,222,215},{214,190,233},{233,177,250},{254,166,248}, - {254,161,228},{254,163,194},{254,171,157},{254,183,125}, - {250,196,108},{229,206,110},{211,211,130},{201,210,164}, - {203,202,202},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - { 75,106, 64},{ 0, 46, 98},{ 0, 22,130},{ 32, 3,126}, - { 65, 0, 88},{ 82, 0, 26},{ 80, 11, 0},{ 59, 34, 0}, - { 24, 58, 0},{ 0, 77, 0},{ 0, 86, 0},{ 0, 83, 0}, - { 0, 68, 39},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {136,180,122},{ 0,101,166},{ 29, 69,208},{ 80, 44,203}, - {123, 32,153},{146, 36, 72},{144, 55, 0},{116, 84, 0}, - { 70,116, 0},{ 19,141, 0},{ 0,153, 0},{ 0,149, 0}, - { 0,130, 90},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {207,254,188},{ 51,183,233},{ 98,151,254},{150,126,254}, - {193,113,220},{217,117,137},{214,137, 45},{186,166, 0}, - {140,198, 0},{ 88,224, 0},{ 44,236, 0},{ 20,232, 63}, - { 23,213,155},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {207,254,188},{144,231,207},{163,218,224},{184,207,222}, - {201,202,201},{211,204,168},{210,212,130},{198,224, 99}, - {180,237, 81},{159,247, 83},{141,252,104},{131,251,137}, - {132,243,175},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - { 83, 83, 55},{ 0, 23, 89},{ 0, 0,121},{ 40, 0,117}, - { 73, 0, 79},{ 90, 0, 17},{ 88, 0, 0},{ 67, 10, 0}, - { 32, 34, 0},{ 0, 53, 0},{ 0, 63, 0},{ 0, 60, 0}, - { 0, 45, 30},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {147,148,110},{ 0, 69,154},{ 40, 38,196},{ 91, 12,191}, - {134, 0,141},{157, 4, 60},{155, 23, 0},{127, 52, 0}, - { 81, 84, 0},{ 30,109, 0},{ 0,121, 0},{ 0,117, 0}, - { 0, 98, 78},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {221,222,173},{ 65,142,217},{112,110,254},{164, 84,255}, - {208, 72,204},{231, 76,122},{229, 95, 29},{200,125, 0}, - {154,157, 0},{102,182, 0},{ 58,195, 0},{ 34,191, 47}, - { 37,171,140},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {221,222,173},{158,189,191},{177,176,208},{198,166,206}, - {216,161,185},{225,163,152},{224,171,114},{213,183, 83}, - {194,195, 66},{173,206, 68},{155,211, 88},{145,209,122}, - {146,201,159},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - { 87, 87,133},{ 0, 26,167},{ 5, 2,198},{ 44, 0,195}, - { 77, 0,157},{ 95, 0, 94},{ 93, 0, 25},{ 71, 14, 0}, - { 36, 38, 0},{ 0, 57, 0},{ 0, 66, 0},{ 0, 63, 38}, - { 0, 49,108},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {153,153,216},{ 0, 74,254},{ 46, 43,254},{ 97, 17,254}, - {140, 5,247},{164, 9,165},{161, 28, 74},{133, 57, 0}, - { 87, 89, 0},{ 36,114, 0},{ 0,126, 10},{ 0,122, 92}, - { 0,103,183},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {229,228,254},{ 74,148,254},{120,116,254},{172, 91,254}, - {216, 78,254},{239, 82,254},{237,102,166},{208,131, 89}, - {162,163, 46},{110,189, 51},{ 66,201,102},{ 42,197,184}, - { 45,178,254},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {229,228,254},{166,196,254},{185,183,254},{206,172,254}, - {224,167,254},{233,169,254},{232,177,252},{221,189,220}, - {202,202,203},{181,212,205},{163,217,226},{153,216,254}, - {154,208,254},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - { 90, 71, 97},{ 0, 11,130},{ 8, 0,162},{ 47, 0,158}, - { 80, 0,120},{ 98, 0, 58},{ 96, 0, 0},{ 74, 0, 0}, - { 39, 22, 0},{ 0, 42, 0},{ 0, 51, 0},{ 0, 48, 2}, - { 0, 33, 72},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {158,132,166},{ 4, 53,210},{ 50, 22,252},{101, 0,247}, - {144, 0,197},{168, 0,116},{165, 7, 25},{137, 36, 0}, - { 91, 68, 0},{ 40, 93, 0},{ 0,105, 0},{ 0,101, 42}, - { 0, 82,134},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {234,201,246},{ 79,121,254},{125, 89,254},{177, 63,254}, - {221, 51,254},{245, 55,195},{242, 74,102},{214,104, 24}, - {167,136, 0},{115,161, 0},{ 71,174, 37},{ 48,170,120}, - { 50,150,213},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {234,201,246},{171,168,254},{190,155,254},{211,145,254}, - {229,140,254},{239,142,225},{237,150,187},{226,162,156}, - {207,174,139},{186,185,141},{168,190,161},{159,188,195}, - {160,180,232},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - { 66, 85, 88},{ 0, 25,121},{ 0, 1,153},{ 23, 0,149}, - { 56, 0,111},{ 74, 0, 49},{ 72, 0, 0},{ 51, 12, 0}, - { 16, 36, 0},{ 0, 55, 0},{ 0, 65, 0},{ 0, 62, 0}, - { 0, 47, 63},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {125,151,154},{ 0, 72,198},{ 17, 40,240},{ 69, 15,235}, - {112, 3,185},{135, 7,104},{132, 26, 12},{104, 55, 0}, - { 59, 87, 0},{ 7,112, 0},{ 0,124, 0},{ 0,120, 30}, - { 0,101,121},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {192,225,230},{ 37,145,254},{ 83,114,254},{135, 88,254}, - {179, 76,254},{202, 80,179},{200, 99, 86},{171,129, 8}, - {125,160, 0},{ 73,186, 0},{ 29,198, 21},{ 5,194,104}, - { 8,175,197},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {192,225,230},{129,193,248},{148,180,254},{169,170,254}, - {187,165,242},{196,166,209},{195,174,171},{184,186,140}, - {165,199,123},{144,209,125},{126,214,145},{116,213,179}, - {118,205,216},{184,184,184},{ 0, 0, 0},{ 0, 0, 0}, - { 69, 69, 69},{ 0, 16,110},{ 0, 0,142},{ 33, 0,138}, - { 66, 0,100},{ 84, 0, 38},{ 82, 0, 0},{ 60, 3, 0}, - { 25, 27, 0},{ 0, 46, 0},{ 0, 56, 0},{ 0, 53, 0}, - { 0, 38, 51},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {134,134,134},{ 0, 64,187},{ 35, 32,228},{ 86, 7,223}, - {129, 0,174},{153, 0, 92},{150, 18, 1},{122, 47, 0}, - { 76, 79, 0},{ 25,104, 0},{ 0,116, 0},{ 0,112, 19}, - { 0, 93,110},{ 0, 0, 0},{ 0, 0, 0},{ 0, 0, 0}, - {207,207,207},{ 60,136,254},{107,104,254},{159, 79,254}, - {203, 66,248},{226, 70,165},{224, 90, 72},{195,119, 0}, - {149,151, 0},{ 97,177, 0},{ 53,189, 8},{ 29,185, 91}, - { 32,166,183},{ 79, 79, 79},{ 0, 0, 0},{ 0, 0, 0}, - {207,207,207},{148,178,229},{166,165,246},{188,155,244}, - {205,150,224},{215,152,190},{214,159,152},{202,171,121}, - {183,184,104},{162,195,106},{145,200,126},{135,198,160}, - {136,190,197},{184,184,184},{ 0, 0, 0},{ 0, 0, 0} -}; - -void Nes_Emu::get_regs(unsigned int *dest) const -{ - dest[0] = emu.r.a; - dest[1] = emu.r.x; - dest[2] = emu.r.y; - dest[3] = emu.r.sp; - dest[4] = emu.r.pc; - dest[5] = emu.r.status; -} diff --git a/quicknes/nes_emu/Nes_Emu.h b/quicknes/nes_emu/Nes_Emu.h deleted file mode 100644 index 1a538f8846..0000000000 --- a/quicknes/nes_emu/Nes_Emu.h +++ /dev/null @@ -1,284 +0,0 @@ - -// NES video game console emulator with snapshot support - -// Nes_Emu 0.7.0 - -#ifndef NES_EMU_H -#define NES_EMU_H - -#include "blargg_common.h" -#include "Multi_Buffer.h" -#include "Nes_Cart.h" -#include "Nes_Core.h" -class Nes_State; - -// Register optional mappers included with Nes_Emu -void register_optional_mappers(); - -extern const char unsupported_mapper []; // returned when cartridge uses unsupported mapper - -class Nes_Emu { -public: - Nes_Emu(); - virtual ~Nes_Emu(); - -// Basic setup - - // Load iNES file into emulator and clear recording - blargg_err_t load_ines( Auto_File_Reader ); - - // Set sample rate for sound generation - blargg_err_t set_sample_rate( long ); - - // Size and depth of graphics buffer required for rendering. Note that this - // is larger than the actual image, with a temporary area around the edge - // that gets filled with junk. Its height is many times larger in Nes_Recorder - // to allow caching of multiple images. - enum { buffer_width = Nes_Ppu::buffer_width }; - int buffer_height() const { return buffer_height_; } - enum { bits_per_pixel = 8 }; - -private: - // Set graphics buffer to render pixels to. Pixels points to top-left pixel and - // row_bytes is the number of bytes to get to the next line (positive or negative). - void set_pixels( void* pixels, long row_bytes ); -public: - - // Size of image generated in graphics buffer - enum { image_width = 256 }; - enum { image_height = 240 }; - -// Basic emulation - - // Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image - // and sound are available for output using the accessors below. - // A connected controller should have 0xffffff** in the high bits, or 0x000000** - // if emulating an incorrectly made third party controller. A disconnected controller - // should be 0x00000000 exactly. - virtual blargg_err_t emulate_frame( uint32_t joypad1, uint32_t joypad2 ); - - // Maximum size of palette that can be generated - enum { max_palette_size = 256 }; - - // Result of current frame - struct frame_t - { - int joypad_read_count; // number of times joypads were strobed (read) - int burst_phase; // NTSC burst phase for frame (0, 1, or 2) - - int sample_count; // number of samples (always a multiple of chan_count) - int chan_count; // 1: mono, 2: stereo - - int top; // top-left position of image in graphics buffer - enum { left = 8 }; - unsigned char* pixels; // pointer to top-left pixel of image - long pitch; // number of bytes to get to next row of image - - int palette_begin; // first host palette entry, as set by set_palette_range() - int palette_size; // number of entries used for current frame - short palette [max_palette_size]; // [palette_begin to palette_begin+palette_size-1] - }; - frame_t const& frame() const { return *frame_; } - - // Read samples for the current frame. Returns number of samples read into buffer. - // Currently all samples must be read in one call. - virtual long read_samples( short* out, long max_samples ); - -// Additional features - - // Use already-loaded cartridge. Retains pointer, so it must be kept around until - // closed. A cartridge can be shared among multiple emulators. After opening, - // cartridge's CHR data shouldn't be modified since a copy is cached internally. - blargg_err_t set_cart( Nes_Cart const* ); - - // Pointer to current cartridge, or NULL if none is loaded - Nes_Cart const* cart() const { return emu.cart; } - - // Free any memory and close cartridge, if one was currently open. A new cartridge - // must be opened before further emulation can take place. - void close(); - - // Emulate powering NES off and then back on. If full_reset is false, emulates - // pressing the reset button only, which doesn't affect memory, otherwise - // emulates powering system off then on. - virtual void reset( bool full_reset = true, bool erase_battery_ram = false ); - - // Number of undefined CPU instructions encountered. Cleared after reset() and - // load_state(). A non-zero value indicates that cartridge is probably - // incompatible. - unsigned long error_count() const { return emu.error_count; } - -// Sound - - // Set sample rate and use a custom sound buffer instead of the default - // mono buffer, i.e. Nes_Buffer, Effects_Buffer, etc.. - blargg_err_t set_sample_rate( long rate, Multi_Buffer* ); - - // Adjust effective frame rate by changing how many samples are generated each frame. - // Allows fine tuning of frame rate to improve synchronization. - void set_frame_rate( double rate ); - - // Number of sound channels for current cartridge - int channel_count() const { return channel_count_; } - - // Frequency equalizer parameters - struct equalizer_t { - double treble; // 5.0 = extra-crisp, -200.0 = muffled - long bass; // 0 = deep, 20000 = tinny - }; - - // Current frequency equalization - equalizer_t const& equalizer() const { return equalizer_; } - - // Change frequency equalization - void set_equalizer( equalizer_t const& ); - - // Equalizer presets - static equalizer_t const nes_eq; // NES - static equalizer_t const famicom_eq; // Famicom - static equalizer_t const tv_eq; // TV speaker - -// File save/load - - // Save emulator state - void save_state( Nes_State* s ) const { emu.save_state( s ); } - blargg_err_t save_state( Auto_File_Writer ) const; - - // Load state into emulator - void load_state( Nes_State const& ); - blargg_err_t load_state( Auto_File_Reader ); - - // True if current cartridge claims it uses battery-backed memory - bool has_battery_ram() const { return cart()->has_battery_ram(); } - - // Save current battery RAM - blargg_err_t save_battery_ram( Auto_File_Writer ); - - // Load battery RAM from file. Best called just after reset() or loading cartridge. - blargg_err_t load_battery_ram( Auto_File_Reader ); - -// Graphics - - // Number of frames generated per second - enum { frame_rate = 60 }; - - // Size of fixed NES color table (including the 8 color emphasis modes) - enum { color_table_size = 8 * 64 }; - - // NES color lookup table based on standard NTSC TV decoder. Use nes_ntsc.h to - // generate a palette with custom parameters. - struct rgb_t { unsigned char red, green, blue; }; - static rgb_t const nes_colors [color_table_size]; - - // Hide/show/enhance sprites. Sprite mode does not affect emulation accuracy. - enum sprite_mode_t { - sprites_hidden = 0, - sprites_visible = 8, // limit of 8 sprites per scanline as on NES (default) - sprites_enhanced = 64 // unlimited sprites per scanline (no flickering) - }; - void set_sprite_mode( sprite_mode_t n ) { emu.ppu.sprite_limit = n; } - - // Set range of host palette entries to use in graphics buffer; default uses - // all of them. Begin will be rounded up to next multiple of palette_alignment. - // Use frame().palette_begin to find the adjusted beginning entry used. - enum { palette_alignment = 64 }; - void set_palette_range( int begin, int end = 256 ); - -// Access to emulated memory, for viewer/cheater/debugger - - // CHR - byte const* chr_mem(); - long chr_size() const; - void write_chr( void const*, long count, long offset ); - - // Nametable - byte* nametable_mem() { return emu.ppu.impl->nt_ram; } - long nametable_size() const { return 0x1000; } - - // Built-in 2K memory - enum { low_mem_size = 0x800 }; - byte* low_mem() { return emu.low_mem; } - - // Optional 8K memory - enum { high_mem_size = 0x2000 }; - byte* high_mem() { return emu.impl->sram; } - -// Prg peek/poke for debuggin - byte peek_prg(nes_addr_t addr) const { return *static_cast(emu).get_code(addr); } - void poke_prg(nes_addr_t addr, byte value) { *static_cast(emu).get_code(addr) = value; } - byte peek_ppu(int addr) { return emu.ppu.peekaddr(addr); } - - void get_regs(unsigned int *dest) const; - - byte get_ppu2000() const { return emu.ppu.w2000; } - byte* pal_mem() { return emu.ppu.palette; } - byte* oam_mem() { return emu.ppu.spr_ram; } - - void set_tracecb(void (*cb)(unsigned int *dest)) { emu.set_tracecb(cb); } - - // End of public interface -public: - blargg_err_t set_sample_rate( long rate, class Nes_Buffer* ); - blargg_err_t set_sample_rate( long rate, class Nes_Effects_Buffer* ); - void irq_changed() { emu.irq_changed(); } -private: - friend class Nes_Recorder; - - frame_t* frame_; - int buffer_height_; - bool fade_sound_in; - bool fade_sound_out; - virtual blargg_err_t init_(); - - virtual void loading_state( Nes_State const& ) { } - void load_state( Nes_State_ const& ); - void save_state( Nes_State_* s ) const { emu.save_state( s ); } - int joypad_read_count() const { return emu.joypad_read_count; } - long timestamp() const { return emu.nes.frame_count; } - void set_timestamp( long t ) { emu.nes.frame_count = t; } - -private: - // noncopyable - Nes_Emu( const Nes_Emu& ); - Nes_Emu& operator = ( const Nes_Emu& ); - - // sound - Multi_Buffer* default_sound_buf; - Multi_Buffer* sound_buf; - unsigned sound_buf_changed_count; - Silent_Buffer silent_buffer; - equalizer_t equalizer_; - int channel_count_; - bool sound_enabled; - void enable_sound( bool ); - void clear_sound_buf(); - void fade_samples( blip_sample_t*, int size, int step ); - - char* host_pixel_buff; - char* host_pixels; - int host_palette_size; - frame_t single_frame; - Nes_Cart private_cart; - Nes_Core emu; // large; keep at end - - bool init_called; - blargg_err_t auto_init(); -}; - -inline void Nes_Emu::set_pixels( void* p, long n ) -{ - host_pixels = (char*) p + n; - emu.ppu.host_row_bytes = n; -} - -inline byte const* Nes_Emu::chr_mem() -{ - return cart()->chr_size() ? (byte*) cart()->chr() : emu.ppu.impl->chr_ram; -} - -inline long Nes_Emu::chr_size() const -{ - return cart()->chr_size() ? cart()->chr_size() : emu.ppu.chr_addr_size; -} - -#endif diff --git a/quicknes/nes_emu/Nes_File.cpp b/quicknes/nes_emu/Nes_File.cpp deleted file mode 100644 index f2f570e61e..0000000000 --- a/quicknes/nes_emu/Nes_File.cpp +++ /dev/null @@ -1,226 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_File.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -// Nes_File_Writer - -Nes_File_Writer::Nes_File_Writer() -{ - write_remain = 0; - depth_ = 0; -} - -Nes_File_Writer::~Nes_File_Writer() -{ -} - -blargg_err_t Nes_File_Writer::begin( Auto_File_Writer dw, nes_tag_t tag ) -{ - require( !out ); - out = dw; - RETURN_ERR( out.open_comp() ); - return begin_group( tag ); -} - -blargg_err_t Nes_File_Writer::begin_group( nes_tag_t tag ) -{ - depth_++; - return write_header( tag, group_begin_size ); -} - -blargg_err_t Nes_File_Writer::write_header( nes_tag_t tag, long size ) -{ - nes_block_t h; - h.tag = tag; - h.size = size; - h.swap(); - return out->write( &h, sizeof h ); -} - -blargg_err_t Nes_File_Writer::write_block( nes_tag_t tag, void const* data, long size ) -{ - RETURN_ERR( write_block_header( tag, size ) ); - return write( data, size ); -} - -blargg_err_t Nes_File_Writer::write_block_header( nes_tag_t tag, long size ) -{ - require( !write_remain ); - write_remain = size; - return write_header( tag, size ); -} - -Nes_File_Writer::error_t Nes_File_Writer::write( void const* p, long s ) -{ - write_remain -= s; - require( write_remain >= 0 ); - return out->write( p, s ); -} - -blargg_err_t Nes_File_Writer::end() -{ - require( depth_ == 1 ); - return end_group(); -} - -blargg_err_t Nes_File_Writer::end_group() -{ - require( depth_ > 0 ); - depth_--; - return write_header( group_end_tag, 0 ); -} - -// Nes_File_Reader - -Nes_File_Reader::Nes_File_Reader() -{ - h.tag = 0; - h.size = 0; - block_type_ = invalid; - depth_ = -1; -} - -Nes_File_Reader::~Nes_File_Reader() -{ -} - -blargg_err_t Nes_File_Reader::read_block_data( void* p, long s ) -{ - long extra = remain(); - if ( s > extra ) - s = extra; - extra -= s; - RETURN_ERR( read( p, s ) ); - if ( extra ) - RETURN_ERR( skip( extra ) ); - return 0; -} - -blargg_err_t Nes_File_Reader::begin( Auto_File_Reader dr ) -{ - require( !in ); - RETURN_ERR( dr.open() ); - in = dr; - RETURN_ERR( read_header() ); - if ( block_type() != group_begin ) - return "File is wrong type"; - return enter_group(); -} - -blargg_err_t Nes_File_Reader::read_header() -{ - RETURN_ERR( in->read( &h, sizeof h ) ); - h.swap(); - block_type_ = data_block; - if ( h.size == group_begin_size ) - { - block_type_ = group_begin; - h.size = 0; - } - if ( (long) h.tag == group_end_tag ) - { - block_type_ = group_end; - h.tag = 0; - } - set_remain( h.size ); - return 0; -} - -blargg_err_t Nes_File_Reader::next_block() -{ - require( depth() >= 0 ); - switch ( block_type() ) - { - case group_end: - require( false ); - return "Tried to go past end of blocks"; - - case group_begin: { - int d = 1; - do - { - RETURN_ERR( skip( h.size ) ); - RETURN_ERR( read_header() ); - if ( block_type() == group_begin ) - d++; - if ( block_type() == group_end ) - d--; - } - while ( d > 0); - break; - } - - case data_block: - RETURN_ERR( skip( h.size ) ); - break; - - case invalid: - break; - } - return read_header(); -} - -blargg_err_t Nes_File_Reader::enter_group() -{ - require( block_type() == group_begin ); - block_type_ = invalid; // cause next_block() not to skip group - depth_++; - return 0; -} - -blargg_err_t Nes_File_Reader::exit_group() -{ - require( depth() > 0 ); - int d = 1; - while ( true ) - { - if ( block_type() == group_end ) - d--; - if ( block_type() == group_begin ) - d++; - if ( d == 0 ) - break; - RETURN_ERR( skip( h.size ) ); - RETURN_ERR( read_header() ); - } - - block_type_ = invalid; // cause next_block() to read past end block - depth_--; - return 0; -} - -blargg_err_t Nes_File_Reader::skip_v( int s ) -{ - require( block_type() == data_block ); - if ( (unsigned long) s > h.size ) - return "Tried to skip past end of data"; - h.size -= s; - set_remain( h.size ); - return in->skip( s ); -} - -blargg_err_t Nes_File_Reader::read_v( void* p, int n ) -{ - require( block_type() == data_block ); - if ( (unsigned long) n > h.size ) - n = h.size; - h.size -= n; - set_remain( h.size ); - return in->read( p, n ); -} diff --git a/quicknes/nes_emu/Nes_File.h b/quicknes/nes_emu/Nes_File.h deleted file mode 100644 index a077dc8e41..0000000000 --- a/quicknes/nes_emu/Nes_File.h +++ /dev/null @@ -1,128 +0,0 @@ - -// NES block-oriented file access - -// Nes_Emu 0.7.0 - -#ifndef NES_FILE_H -#define NES_FILE_H - -#include "blargg_common.h" -#include "abstract_file.h" -#include "nes_data.h" - -// Writes a structured file -class Nes_File_Writer : public Data_Writer { -public: - Nes_File_Writer(); - ~Nes_File_Writer(); - - // Begin writing file with specified signature tag - blargg_err_t begin( Auto_File_Writer, nes_tag_t ); - - // Begin tagged group - blargg_err_t begin_group( nes_tag_t ); - - // Write tagged block - blargg_err_t write_block( nes_tag_t, void const*, long size ); - - // Write tagged block header. 'Size' bytes must be written before next block. - blargg_err_t write_block_header( nes_tag_t, long size ); - - // Write data to current block - error_t write( void const*, long ); - - // End tagged group - blargg_err_t end_group(); - - // End file - blargg_err_t end(); - -private: - Auto_File_Writer out; - long write_remain; - int depth_; - blargg_err_t write_header( nes_tag_t tag, long size ); -}; - -// Reads a structured file -class Nes_File_Reader : public Data_Reader { -public: - Nes_File_Reader(); - ~Nes_File_Reader(); - - // If true, verify checksums of any blocks that have them - void enable_checksums( bool = true ); - - // Begin reading file. Until next_block() is called, block_tag() yields tag for file. - blargg_err_t begin( Auto_File_Reader ); - - // Read header of next block in current group - blargg_err_t next_block(); - - // Type of current block - enum block_type_t { - data_block, - group_begin, - group_end, - invalid - }; - block_type_t block_type() const { return block_type_; } - - // Tag of current block - nes_tag_t block_tag() const { return h.tag; } - - // Read at most s bytes from block and skip any remaining bytes - blargg_err_t read_block_data( void*, long s ); - - // Read at most 's' bytes from current block and return number of bytes actually read - virtual blargg_err_t read_v( void*, int n ); - - // Skip 's' bytes in current block - virtual blargg_err_t skip_v( int s ); - - // Read first sub-block of current group block - blargg_err_t enter_group(); - - // Skip past current group - blargg_err_t exit_group(); - - // Current depth, where 0 is top-level in file and higher is deeper - int depth() const { return depth_; } - - // True if all data has been read - bool done() const { return depth() == 0 && block_type() == group_end; } -private: - Auto_File_Reader in; - nes_block_t h; - block_type_t block_type_; - int depth_; - blargg_err_t read_header(); -}; - -template -inline blargg_err_t read_nes_state( Nes_File_Reader& in, T* out ) -{ - blargg_err_t err = in.read_block_data( out, sizeof *out ); - out->swap(); - return err; -} - -template -inline blargg_err_t write_nes_state( Nes_File_Writer& out, T& in ) -{ - in.swap(); - blargg_err_t err = out.write_block( in.tag, &in, sizeof in ); - in.swap(); - return err; -} - -template -inline blargg_err_t write_nes_state( Nes_File_Writer& out, const T& in ) -{ - T copy = in; - copy.swap(); - return out.write_block( copy.tag, ©, sizeof copy ); -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Fme7_Apu.cpp b/quicknes/nes_emu/Nes_Fme7_Apu.cpp deleted file mode 100644 index 2cb8d0cd4b..0000000000 --- a/quicknes/nes_emu/Nes_Fme7_Apu.cpp +++ /dev/null @@ -1,120 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Fme7_Apu.h" - -#include - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -void Nes_Fme7_Apu::reset() -{ - last_time = 0; - - for ( int i = 0; i < osc_count; i++ ) - oscs [i].last_amp = 0; - - fme7_apu_state_t* state = this; - memset( state, 0, sizeof *state ); -} - -unsigned char Nes_Fme7_Apu::amp_table [16] = -{ - #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5) - ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), - ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), - ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), - ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000) - #undef ENTRY -}; - -void Nes_Fme7_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_time ); - - for ( int index = 0; index < osc_count; index++ ) - { - int mode = regs [7] >> index; - int vol_mode = regs [010 + index]; - int volume = amp_table [vol_mode & 0x0f]; - - if ( !oscs [index].output ) - continue; - - // check for unsupported mode - #ifndef NDEBUG - if ( (mode & 011) <= 001 && vol_mode & 0x1f ) - dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", - mode, vol_mode & 0x1f ); - #endif - - if ( (mode & 001) | (vol_mode & 0x10) ) - volume = 0; // noise and envelope aren't supported - - // period - int const period_factor = 16; - unsigned period = (regs [index * 2 + 1] & 0x0f) * 0x100 * period_factor + - regs [index * 2] * period_factor; - if ( period < 50 ) // around 22 kHz - { - volume = 0; - if ( !period ) // on my AY-3-8910A, period doesn't have extra one added - period = period_factor; - } - - // current amplitude - int amp = volume; - if ( !phases [index] ) - amp = 0; - int delta = amp - oscs [index].last_amp; - if ( delta ) - { - oscs [index].last_amp = amp; - synth.offset( last_time, delta, oscs [index].output ); - } - - blip_time_t time = last_time + delays [index]; - if ( time < end_time ) - { - Blip_Buffer* const osc_output = oscs [index].output; - int delta = amp * 2 - volume; - - if ( volume ) - { - do - { - delta = -delta; - synth.offset_inline( time, delta, osc_output ); - time += period; - } - while ( time < end_time ); - - oscs [index].last_amp = (delta + volume) >> 1; - phases [index] = (delta > 0); - } - else - { - // maintain phase when silent - int count = (end_time - time + period - 1) / period; - phases [index] ^= count & 1; - time += (long) count * period; - } - } - - delays [index] = time - end_time; - } - - last_time = end_time; -} - diff --git a/quicknes/nes_emu/Nes_Fme7_Apu.h b/quicknes/nes_emu/Nes_Fme7_Apu.h deleted file mode 100644 index c8f5f32563..0000000000 --- a/quicknes/nes_emu/Nes_Fme7_Apu.h +++ /dev/null @@ -1,135 +0,0 @@ - -// Sunsoft FME-7 sound emulator - -// Nes_Emu 0.7.0 - -#ifndef NES_FME7_APU_H -#define NES_FME7_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct fme7_apu_state_t -{ - enum { reg_count = 14 }; - BOOST::uint8_t regs [reg_count]; - BOOST::uint8_t phases [3]; // 0 or 1 - BOOST::uint8_t latch; - BOOST::uint16_t delays [3]; // a, b, c -}; -BOOST_STATIC_ASSERT( sizeof (fme7_apu_state_t) == 24 ); - -class Nes_Fme7_Apu : private fme7_apu_state_t { -public: - Nes_Fme7_Apu(); - - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); - enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); - void end_frame( blip_time_t ); - void save_state( fme7_apu_state_t* ) const; - void load_state( fme7_apu_state_t const& ); - - // Mask and addresses of registers - enum { addr_mask = 0xe000 }; - enum { data_addr = 0xe000 }; - enum { latch_addr = 0xc000 }; - - // (addr & addr_mask) == latch_addr - void write_latch( int ); - - // (addr & addr_mask) == data_addr - void write_data( blip_time_t, int data ); - - // End of public interface -private: - // noncopyable - Nes_Fme7_Apu( const Nes_Fme7_Apu& ); - Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); - - static unsigned char amp_table [16]; - - struct { - Blip_Buffer* output; - int last_amp; - } oscs [osc_count]; - blip_time_t last_time; - - enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff - Blip_Synth synth; - - void run_until( blip_time_t ); -}; - -inline void Nes_Fme7_Apu::volume( double v ) -{ - synth.volume( 0.38 / amp_range * v ); // to do: fine-tune -} - -inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq ) -{ - synth.treble_eq( eq ); -} - -inline void Nes_Fme7_Apu::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -inline Nes_Fme7_Apu::Nes_Fme7_Apu() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; } - -inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data ) -{ - if ( (unsigned) latch >= reg_count ) - { - #ifdef dprintf - dprintf( "FME7 write to %02X (past end of sound registers)\n", (int) latch ); - #endif - return; - } - - run_until( time ); - regs [latch] = data; -} - -inline void Nes_Fme7_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const -{ - *out = *this; -} - -inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in ) -{ - reset(); - fme7_apu_state_t* state = this; - *state = in; -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Mapper.cpp b/quicknes/nes_emu/Nes_Mapper.cpp deleted file mode 100644 index eaef1c99bd..0000000000 --- a/quicknes/nes_emu/Nes_Mapper.cpp +++ /dev/null @@ -1,222 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -#include -#include "Nes_Core.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -Nes_Mapper::Nes_Mapper() -{ - emu_ = NULL; - static char c; - state = &c; // TODO: state must not be null? - state_size = 0; -} - -Nes_Mapper::~Nes_Mapper() -{ -} - -// Sets mirroring, maps first 8K CHR in, first and last 16K of PRG, -// intercepts writes to upper half of memory, and clears registered state. -void Nes_Mapper::default_reset_state() -{ - int mirroring = cart_->mirroring(); - if ( mirroring & 8 ) - mirror_full(); - else if ( mirroring & 1 ) - mirror_vert(); - else - mirror_horiz(); - - set_chr_bank( 0, bank_8k, 0 ); - - set_prg_bank( 0x8000, bank_16k, 0 ); - set_prg_bank( 0xC000, bank_16k, last_bank ); - - intercept_writes( 0x8000, 0x8000 ); - - memset( state, 0, state_size ); -} - -void Nes_Mapper::reset() -{ - default_reset_state(); - reset_state(); - apply_mapping(); -} - -void mapper_state_t::write( const void* p, unsigned long s ) -{ - require( s <= max_mapper_state_size ); - require( !size ); - size = s; - memcpy( data, p, s ); -} - -int mapper_state_t::read( void* p, unsigned long s ) const -{ - if ( (long) s > size ) - s = size; - memcpy( p, data, s ); - return s; -} - -void Nes_Mapper::save_state( mapper_state_t& out ) -{ - out.write( state, state_size ); -} - -void Nes_Mapper::load_state( mapper_state_t const& in ) -{ - default_reset_state(); - read_state( in ); - apply_mapping(); -} - -void Nes_Mapper::read_state( mapper_state_t const& in ) -{ - memset( state, 0, state_size ); - in.read( state, state_size ); - apply_mapping(); -} - -// Timing - -void Nes_Mapper::irq_changed() { emu_->irq_changed(); } - -nes_time_t Nes_Mapper::next_irq( nes_time_t ) { return no_irq; } - -void Nes_Mapper::a12_clocked() { } - -void Nes_Mapper::run_until( nes_time_t ) { } - -void Nes_Mapper::end_frame( nes_time_t ) { } - -bool Nes_Mapper::ppu_enabled() const { return emu().ppu.w2001 & 0x08; } - -// Sound - -int Nes_Mapper::channel_count() const { return 0; } - -void Nes_Mapper::set_channel_buf( int, Blip_Buffer* ) { require( false ); } - -void Nes_Mapper::set_treble( blip_eq_t const& ) { } - -// Memory mapping - -void Nes_Mapper::set_prg_bank( nes_addr_t addr, bank_size_t bs, int bank ) -{ - require( addr >= 0x2000 ); // can't remap low-memory - - int bank_size = 1 << bs; - require( addr % bank_size == 0 ); // must be aligned - - int bank_count = cart_->prg_size() >> bs; - if ( bank < 0 ) - bank += bank_count; - - if ( bank >= bank_count ) - { - check( !(cart_->prg_size() & (cart_->prg_size() - 1)) ); // ensure PRG size is power of 2 - bank %= bank_count; - } - - emu().map_code( addr, bank_size, cart_->prg() + (bank << bs) ); - - if ( unsigned (addr - 0x6000) < 0x2000 ) - emu().enable_prg_6000(); -} - -void Nes_Mapper::set_chr_bank( nes_addr_t addr, bank_size_t bs, int bank ) -{ - emu().ppu.render_until( emu().clock() ); - emu().ppu.set_chr_bank( addr, 1 << bs, bank << bs ); -} - -void Nes_Mapper::set_chr_bank_ex( nes_addr_t addr, bank_size_t bs, int bank ) -{ - emu().ppu.render_until( emu().clock() ); - emu().ppu.set_chr_bank_ex( addr, 1 << bs, bank << bs ); -} - -void Nes_Mapper::mirror_manual( int page0, int page1, int page2, int page3 ) -{ - emu().ppu.render_bg_until( emu().clock() ); - emu().ppu.set_nt_banks( page0, page1, page2, page3 ); -} - -#ifndef NDEBUG -int Nes_Mapper::handle_bus_conflict( nes_addr_t addr, int data ) -{ - if ( emu().Nes_Cpu::get_code( addr ) [0] != data ) - dprintf( "Mapper write had bus conflict\n" ); - return data; -} -#endif - -// Mapper registration - -int const max_mappers = 32; -Nes_Mapper::mapping_t Nes_Mapper::mappers [max_mappers] = -{ - { 0, Nes_Mapper::make_nrom }, - { 1, Nes_Mapper::make_mmc1 }, - { 2, Nes_Mapper::make_unrom }, - { 3, Nes_Mapper::make_cnrom }, - { 4, Nes_Mapper::make_mmc3 }, - { 7, Nes_Mapper::make_aorom } -}; -static int mapper_count = 6; // to do: keep synchronized with pre-supplied mappers above - -Nes_Mapper::creator_func_t Nes_Mapper::get_mapper_creator( int code ) -{ - for ( int i = 0; i < mapper_count; i++ ) - { - if ( mappers [i].code == code ) - return mappers [i].func; - } - return NULL; -} - -void Nes_Mapper::register_mapper( int code, creator_func_t func ) -{ - // Catch attempted registration of a different creation function for same mapper code - require( !get_mapper_creator( code ) || get_mapper_creator( code ) == func ); - require( mapper_count < max_mappers ); // fixed liming on number of registered mappers - - mapping_t& m = mappers [mapper_count++]; - m.code = code; - m.func = func; -} - -Nes_Mapper* Nes_Mapper::create( Nes_Cart const* cart, Nes_Core* emu ) -{ - Nes_Mapper::creator_func_t func = get_mapper_creator( cart->mapper_code() ); - if ( !func ) - return NULL; - - // to do: out of memory will be reported as unsupported mapper - Nes_Mapper* mapper = func(); - if ( mapper ) - { - mapper->cart_ = cart; - mapper->emu_ = emu; - } - return mapper; -} - diff --git a/quicknes/nes_emu/Nes_Mapper.h b/quicknes/nes_emu/Nes_Mapper.h deleted file mode 100644 index 27669d7116..0000000000 --- a/quicknes/nes_emu/Nes_Mapper.h +++ /dev/null @@ -1,235 +0,0 @@ - -// NES mapper interface - -// Nes_Emu 0.7.0 - -#ifndef NES_MAPPER -#define NES_MAPPER - -#include "Nes_Cart.h" -#include "Nes_Cpu.h" -#include "nes_data.h" -#include "Nes_Core.h" -class Blip_Buffer; -class blip_eq_t; -class Nes_Core; - -class Nes_Mapper { -public: - // Register function that creates mapper for given code. - typedef Nes_Mapper* (*creator_func_t)(); - static void register_mapper( int code, creator_func_t ); - - // Register optional mappers included with Nes_Emu - void register_optional_mappers(); - - // Create mapper appropriate for cartridge. Returns NULL if it uses unsupported mapper. - static Nes_Mapper* create( Nes_Cart const*, Nes_Core* ); - - virtual ~Nes_Mapper(); - - // Reset mapper to power-up state. - virtual void reset(); - - // Save snapshot of mapper state. Default saves registered state. - virtual void save_state( mapper_state_t& ); - - // Resets mapper, loads state, then applies it - virtual void load_state( mapper_state_t const& ); - -// I/O - - // Read from memory - virtual int read( nes_time_t, nes_addr_t ); - - // Write to memory - virtual void write( nes_time_t, nes_addr_t, int data ) = 0; - - // Write to memory below 0x8000 (returns false if mapper didn't handle write) - virtual bool write_intercepted( nes_time_t, nes_addr_t, int data ); - -// Timing - - // Time returned when current mapper state won't ever cause an IRQ - enum { no_irq = LONG_MAX / 2 }; - - // Time next IRQ will occur at - virtual nes_time_t next_irq( nes_time_t present ); - - // Run mapper until given time - virtual void run_until( nes_time_t ); - - // End video frame of given length - virtual void end_frame( nes_time_t length ); - -// Sound - - // Number of sound channels - virtual int channel_count() const; - - // Set sound buffer for channel to output to, or NULL to silence channel. - virtual void set_channel_buf( int index, Blip_Buffer* ); - - // Set treble equalization - virtual void set_treble( blip_eq_t const& ); - -// Misc - - // Called when bit 12 of PPU's VRAM address changes from 0 to 1 due to - // $2006 and $2007 accesses (but not due to PPU scanline rendering). - virtual void a12_clocked(); - -protected: - // Services provided for derived mapper classes - Nes_Mapper(); - - // Register state data to automatically save and load. Be sure the binary - // layout is suitable for use in a file, including any byte-order issues. - // Automatically cleared to zero by default reset(). - void register_state( void*, unsigned ); - - // Enable 8K of RAM at 0x6000-0x7FFF, optionally read-only. - void enable_sram( bool enabled = true, bool read_only = false ); - - // Cause CPU writes within given address range to call mapper's write() function. - // Might map a larger address range, which the mapper can ignore and pass to - // Nes_Mapper::write(). The range 0x8000-0xffff is always intercepted by the mapper. - void intercept_writes( nes_addr_t addr, unsigned size ); - - // Cause CPU reads within given address range to call mapper's read() function. - // Might map a larger address range, which the mapper can ignore and pass to - // Nes_Mapper::read(). CPU opcode/operand reads and low-memory reads always - // go directly to memory and cannot be intercepted. - void intercept_reads( nes_addr_t addr, unsigned size ); - - // Bank sizes for mapping - enum bank_size_t { // 1 << bank_Xk = X * 1024 - bank_1k = 10, - bank_2k = 11, - bank_4k = 12, - bank_8k = 13, - bank_16k = 14, - bank_32k = 15 - }; - - // Index of last PRG/CHR bank. Last_bank selects last bank, last_bank - 1 - // selects next-to-last bank, etc. - enum { last_bank = -1 }; - - // Map 'size' bytes from 'PRG + bank * size' to CPU address space starting at 'addr' - void set_prg_bank( nes_addr_t addr, bank_size_t size, int bank ); - - // Map 'size' bytes from 'CHR + bank * size' to PPU address space starting at 'addr' - void set_chr_bank( nes_addr_t addr, bank_size_t size, int bank ); - void set_chr_bank_ex( nes_addr_t addr, bank_size_t size, int bank ); // mmc24 only - - // Set PPU mirroring. All mappings implemented using mirror_manual(). - void mirror_manual( int page0, int page1, int page2, int page3 ); - void mirror_single( int page ); - void mirror_horiz( int page = 0 ); - void mirror_vert( int page = 0 ); - void mirror_full(); - - // True if PPU rendering is enabled. Some mappers watch PPU memory accesses to determine - // when scanlines occur, and can only do this when rendering is enabled. - bool ppu_enabled() const; - - // Cartridge being emulated - Nes_Cart const& cart() const { return *cart_; } - - // Must be called when next_irq()'s return value is earlier than previous, - // current CPU run can be stopped earlier. Best to call whenever time may - // have changed (no performance impact if called even when time didn't change). - void irq_changed(); - - // Handle data written to mapper that doesn't handle bus conflict arising due to - // PRG also reading data. Returns data that mapper should act as if were - // written. Currently always returns 'data' and just checks that data written is - // the same as byte in PRG at same address and writes debug message if it doesn't. - int handle_bus_conflict( nes_addr_t addr, int data ); - - // Reference to emulator that uses this mapper. - Nes_Core& emu() const { return *emu_; } - -protected: - // Services derived classes provide - - // Read state from snapshot. Default reads data into registered state, then calls - // apply_mapping(). - virtual void read_state( mapper_state_t const& ); - - // Apply current mapping state to hardware. Called after reading mapper state - // from a snapshot. - virtual void apply_mapping() = 0; - - // Called by default reset() before apply_mapping() is called. - virtual void reset_state() { } - - // End of general interface -private: - Nes_Core* emu_; - void* state; - unsigned state_size; - Nes_Cart const* cart_; - - void default_reset_state(); - - struct mapping_t { - int code; - Nes_Mapper::creator_func_t func; - }; - static mapping_t mappers []; - static creator_func_t get_mapper_creator( int code ); - - // built-in mappers - static Nes_Mapper* make_nrom(); - static Nes_Mapper* make_unrom(); - static Nes_Mapper* make_aorom(); - static Nes_Mapper* make_cnrom(); - static Nes_Mapper* make_mmc1(); - static Nes_Mapper* make_mmc3(); -}; - -template -struct register_mapper { - /*void*/ register_mapper( int code ) { Nes_Mapper::register_mapper( code, create ); } - static Nes_Mapper* create() { return BLARGG_NEW T; } -}; - -#ifdef NDEBUG -inline int Nes_Mapper::handle_bus_conflict( nes_addr_t addr, int data ) { return data; } -#endif - -inline void Nes_Mapper::mirror_horiz( int p ) { mirror_manual( p, p, p ^ 1, p ^ 1 ); } -inline void Nes_Mapper::mirror_vert( int p ) { mirror_manual( p, p ^ 1, p, p ^ 1 ); } -inline void Nes_Mapper::mirror_single( int p ) { mirror_manual( p, p, p, p ); } -inline void Nes_Mapper::mirror_full() { mirror_manual( 0, 1, 2, 3 ); } - -inline void Nes_Mapper::register_state( void* p, unsigned s ) -{ - assert( s <= max_mapper_state_size ); - state = p; - state_size = s; -} - -inline bool Nes_Mapper::write_intercepted( nes_time_t, nes_addr_t, int ) { return false; } - -inline int Nes_Mapper::read( nes_time_t, nes_addr_t ) { return -1; } // signal to caller - -inline void Nes_Mapper::intercept_reads( nes_addr_t addr, unsigned size ) -{ - emu().add_mapper_intercept( addr, size, true, false ); -} - -inline void Nes_Mapper::intercept_writes( nes_addr_t addr, unsigned size ) -{ - emu().add_mapper_intercept( addr, size, false, true ); -} - -inline void Nes_Mapper::enable_sram( bool enabled, bool read_only ) -{ - emu_->enable_sram( enabled, read_only ); -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Mmc1.cpp b/quicknes/nes_emu/Nes_Mmc1.cpp deleted file mode 100644 index e00770e288..0000000000 --- a/quicknes/nes_emu/Nes_Mmc1.cpp +++ /dev/null @@ -1,123 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -#include - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -class Mapper_Mmc1 : public Nes_Mapper, mmc1_state_t { -public: - Mapper_Mmc1() - { - mmc1_state_t* state = this; - register_state( state, sizeof *state ); - } - - virtual void reset_state() - { - regs [0] = 0x0f; - regs [1] = 0x00; - regs [2] = 0x01; - regs [3] = 0x00; - } - - void register_changed( int ); - - virtual void apply_mapping() - { - enable_sram(); // early MMC1 always had SRAM enabled - register_changed( 0 ); - } - - virtual void write( nes_time_t, nes_addr_t addr, int data ) - { - if ( !(data & 0x80) ) - { - buf |= (data & 1) << bit; - bit++; - - if ( bit >= 5 ) - { - int reg = addr >> 13 & 3; - regs [reg] = buf & 0x1f; - - bit = 0; - buf = 0; - - register_changed( reg ); - } - } - else - { - bit = 0; - buf = 0; - regs [0] |= 0x0c; - register_changed( 0 ); - } - } -}; - -void Mapper_Mmc1::register_changed( int reg ) -{ - // Mirroring - if ( reg == 0 ) - { - int mode = regs [0] & 3; - if ( mode < 2 ) - mirror_single( mode & 1 ); - else if ( mode == 2 ) - mirror_vert(); - else - mirror_horiz(); - } - - // CHR - if ( reg < 3 && cart().chr_size() > 0 ) - { - if ( regs [0] & 0x10 ) - { - set_chr_bank( 0x0000, bank_4k, regs [1] ); - set_chr_bank( 0x1000, bank_4k, regs [2] ); - } - else - { - set_chr_bank( 0, bank_8k, regs [1] >> 1 ); - } - } - - // PRG - int bank = (regs [1] & 0x10) | (regs [3] & 0x0f); - if ( !(regs [0] & 0x08) ) - { - set_prg_bank( 0x8000, bank_32k, bank >> 1 ); - } - else if ( regs [0] & 0x04 ) - { - set_prg_bank( 0x8000, bank_16k, bank ); - set_prg_bank( 0xC000, bank_16k, bank | 0x0f ); - } - else - { - set_prg_bank( 0x8000, bank_16k, bank & ~0x0f ); - set_prg_bank( 0xC000, bank_16k, bank ); - } -} - -Nes_Mapper* Nes_Mapper::make_mmc1() -{ - return BLARGG_NEW Mapper_Mmc1; -} - diff --git a/quicknes/nes_emu/Nes_Mmc3.cpp b/quicknes/nes_emu/Nes_Mmc3.cpp deleted file mode 100644 index e23ffa95fd..0000000000 --- a/quicknes/nes_emu/Nes_Mmc3.cpp +++ /dev/null @@ -1,252 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -#include -#include "Nes_Core.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -// 264 or less breaks Gargoyle's Quest II -// 267 or less breaks Magician -int const irq_fine_tune = 268; -nes_time_t const first_scanline = 20 * Nes_Ppu::scanline_len + irq_fine_tune; -nes_time_t const last_scanline = first_scanline + 240 * Nes_Ppu::scanline_len; - -class Mapper_Mmc3 : public Nes_Mapper, mmc3_state_t { - nes_time_t next_time; - int counter_just_clocked; // used only for debugging -public: - Mapper_Mmc3() - { - mmc3_state_t* state = this; - register_state( state, sizeof *state ); - } - - virtual void reset_state() - { - memcpy( banks, "\0\2\4\5\6\7\0\1", sizeof banks ); - - counter_just_clocked = 0; - next_time = 0; - mirror = 1; - if ( cart().mirroring() & 1 ) - { - mirror = 0; - //dprintf( "cart specified vertical mirroring\n" ); - } - } - - void update_chr_banks(); - void update_prg_banks(); - void write_irq( nes_addr_t addr, int data ); - void write( nes_time_t, nes_addr_t, int ); - - void start_frame() { next_time = first_scanline; } - - virtual void apply_mapping() - { - write( 0, 0xA000, mirror ); - write( 0, 0xA001, sram_mode ); - update_chr_banks(); - update_prg_banks(); - start_frame(); - } - - void clock_counter() - { - if ( counter_just_clocked ) - counter_just_clocked--; - - if ( !irq_ctr-- ) - { - irq_ctr = irq_latch; - //if ( !irq_latch ) - //dprintf( "MMC3 IRQ counter reloaded with 0\n" ); - } - - //dprintf( "%6d MMC3 IRQ clocked\n", time / ppu_overclock ); - if ( irq_ctr == 0 ) - { - //if ( irq_enabled && !irq_flag ) - //dprintf( "%6d MMC3 IRQ triggered: %f\n", time / ppu_overclock, time / scanline_len.0 - 20 ); - irq_flag = irq_enabled; - } - } - - virtual void run_until( nes_time_t ); - - virtual void a12_clocked() - { - clock_counter(); - if ( irq_enabled ) - irq_changed(); - } - - virtual void end_frame( nes_time_t end_time ) - { - run_until( end_time ); - start_frame(); - } - - virtual nes_time_t next_irq( nes_time_t present ) - { - run_until( present ); - - if ( !irq_enabled ) - return no_irq; - - if ( irq_flag ) - return 0; - - if ( !ppu_enabled() ) - return no_irq; - - int remain = irq_ctr - 1; - if ( remain < 0 ) - remain = irq_latch; - - assert( remain >= 0 ); - - long time = remain * 341L + next_time; - if ( time > last_scanline ) - return no_irq; - - return time / ppu_overclock + 1; - } -}; - -void Mapper_Mmc3::run_until( nes_time_t end_time ) -{ - bool bg_enabled = ppu_enabled(); - - end_time *= ppu_overclock; - while ( next_time < end_time && next_time <= last_scanline ) - { - if ( bg_enabled ) - clock_counter(); - next_time += Nes_Ppu::scanline_len; - } -} - -void Mapper_Mmc3::update_chr_banks() -{ - int chr_xor = (mode >> 7 & 1) * 0x1000; - set_chr_bank( 0x0000 ^ chr_xor, bank_2k, banks [0] >> 1 ); - set_chr_bank( 0x0800 ^ chr_xor, bank_2k, banks [1] >> 1 ); - set_chr_bank( 0x1000 ^ chr_xor, bank_1k, banks [2] ); - set_chr_bank( 0x1400 ^ chr_xor, bank_1k, banks [3] ); - set_chr_bank( 0x1800 ^ chr_xor, bank_1k, banks [4] ); - set_chr_bank( 0x1c00 ^ chr_xor, bank_1k, banks [5] ); -} - -void Mapper_Mmc3::update_prg_banks() -{ - set_prg_bank( 0xA000, bank_8k, banks [7] ); - nes_addr_t addr = 0x8000 + 0x4000 * (mode >> 6 & 1); - set_prg_bank( addr, bank_8k, banks [6] ); - set_prg_bank( addr ^ 0x4000, bank_8k, last_bank - 1 ); -} - -void Mapper_Mmc3::write_irq( nes_addr_t addr, int data ) -{ - switch ( addr & 0xE001 ) - { - case 0xC000: - irq_latch = data; - break; - - case 0xC001: - if ( counter_just_clocked == 1 ) - dprintf( "MMC3 IRQ counter pathological behavior triggered\n" ); - counter_just_clocked = 2; - irq_ctr = 0; - break; - - case 0xE000: - irq_flag = false; - irq_enabled = false; - break; - - case 0xE001: - irq_enabled = true; - break; - } - if ( irq_enabled ) - irq_changed(); -} - -void Mapper_Mmc3::write( nes_time_t time, nes_addr_t addr, int data ) -{ - check( !(addr & ~0xe001) ); // writes to mirrored registers are rare - //dprintf( "%6d %02X->%04X\n", time, data, addr ); - - switch ( addr & 0xE001 ) - { - case 0x8000: { - int changed = mode ^ data; - mode = data; - // avoid unnecessary bank updates - if ( changed & 0x80 ) - update_chr_banks(); - if ( changed & 0x40 ) - update_prg_banks(); - break; - } - - case 0x8001: { - int bank = mode & 7; - banks [bank] = data; - if ( bank < 6 ) - update_chr_banks(); - else - update_prg_banks(); - break; - } - - case 0xA000: - mirror = data; - if ( !(cart().mirroring() & 0x08) ) - { - if ( mirror & 1 ) - mirror_horiz(); - else - mirror_vert(); - } - break; - - case 0xA001: - sram_mode = data; - //dprintf( "%02X->%04X\n", data, addr ); - - // Startropics 1 & 2 use MMC6 and always enable low 512 bytes of SRAM - if ( (data & 0x3F) == 0x30 ) - enable_sram( true ); - else - enable_sram( data & 0x80, data & 0x40 ); - break; - - default: - run_until( time ); - write_irq( addr, data ); - break; - } -} - -Nes_Mapper* Nes_Mapper::make_mmc3() -{ - return BLARGG_NEW Mapper_Mmc3; -} - diff --git a/quicknes/nes_emu/Nes_Namco_Apu.cpp b/quicknes/nes_emu/Nes_Namco_Apu.cpp deleted file mode 100644 index a15f2af9ef..0000000000 --- a/quicknes/nes_emu/Nes_Namco_Apu.cpp +++ /dev/null @@ -1,149 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Nes_Namco_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -Nes_Namco_Apu::Nes_Namco_Apu() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -Nes_Namco_Apu::~Nes_Namco_Apu() -{ -} - -void Nes_Namco_Apu::reset() -{ - last_time = 0; - addr_reg = 0; - - int i; - for ( i = 0; i < reg_count; i++ ) - reg [i] = 0; - - for ( i = 0; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - osc.delay = 0; - osc.last_amp = 0; - osc.wave_pos = 0; - } -} - -void Nes_Namco_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -/* -void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) -{ - reflect_int16( data, 'ADDR', &addr_reg ); - - static const char hex [17] = "0123456789ABCDEF"; - int i; - for ( i = 0; i < reg_count; i++ ) - reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); - - for ( i = 0; i < osc_count; i++ ) - { - reflect_int32( data, 'DLY0' + i, &oscs [i].delay ); - reflect_int16( data, 'POS0' + i, &oscs [i].wave_pos ); - } -} -*/ - -void Nes_Namco_Apu::end_frame( nes_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -void Nes_Namco_Apu::run_until( nes_time_t nes_end_time ) -{ - int active_oscs = (reg [0x7F] >> 4 & 7) + 1; - for ( int i = osc_count - active_oscs; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - Blip_Buffer* output = osc.output; - if ( !output ) - continue; - - blip_resampled_time_t time = - output->resampled_time( last_time ) + osc.delay; - blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); - osc.delay = 0; - if ( time < end_time ) - { - const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; - if ( !(osc_reg [4] & 0xE0) ) - continue; - - int volume = osc_reg [7] & 15; - if ( !volume ) - continue; - - long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; - if ( freq < 64 * active_oscs ) - continue; // prevent low frequencies from excessively delaying freq changes - blip_resampled_time_t period = - output->resampled_duration( 983040 ) / freq * active_oscs; - - int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; - if ( !wave_size ) - continue; - - int last_amp = osc.last_amp; - int wave_pos = osc.wave_pos; - - do - { - // read wave sample - int addr = wave_pos + osc_reg [6]; - int sample = reg [addr >> 1] >> (addr << 2 & 4); - wave_pos++; - sample = (sample & 15) * volume; - - // output impulse if amplitude changed - int delta = sample - last_amp; - if ( delta ) - { - last_amp = sample; - synth.offset_resampled( time, delta, output ); - } - - // next sample - time += period; - if ( wave_pos >= wave_size ) - wave_pos = 0; - } - while ( time < end_time ); - - osc.wave_pos = wave_pos; - osc.last_amp = last_amp; - } - osc.delay = time - end_time; - } - - last_time = nes_end_time; -} - diff --git a/quicknes/nes_emu/Nes_Namco_Apu.h b/quicknes/nes_emu/Nes_Namco_Apu.h deleted file mode 100644 index e22433c695..0000000000 --- a/quicknes/nes_emu/Nes_Namco_Apu.h +++ /dev/null @@ -1,104 +0,0 @@ - -// Namco 106 sound chip emulator - -// Nes_Snd_Emu 0.1.7 - -#ifndef NES_NAMCO_APU_H -#define NES_NAMCO_APU_H - -#include "Nes_Apu.h" - -struct namco_state_t; - -class Nes_Namco_Apu { -public: - Nes_Namco_Apu(); - ~Nes_Namco_Apu(); - - // See Nes_Apu.h for reference. - void volume( double ); - void treble_eq( const blip_eq_t& ); - void output( Blip_Buffer* ); - enum { osc_count = 8 }; - void osc_output( int index, Blip_Buffer* ); - void reset(); - void end_frame( nes_time_t ); - - // Read/write data register is at 0x4800 - enum { data_reg_addr = 0x4800 }; - void write_data( nes_time_t, int ); - int read_data(); - - // Write-only address register is at 0xF800 - enum { addr_reg_addr = 0xF800 }; - void write_addr( int ); - - // to do: implement save/restore - void save_state( namco_state_t* out ) const; - void load_state( namco_state_t const& ); - -private: - // noncopyable - Nes_Namco_Apu( const Nes_Namco_Apu& ); - Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); - - struct Namco_Osc { - long delay; - Blip_Buffer* output; - short last_amp; - short wave_pos; - }; - - Namco_Osc oscs [osc_count]; - - nes_time_t last_time; - int addr_reg; - - enum { reg_count = 0x80 }; - BOOST::uint8_t reg [reg_count]; - Blip_Synth synth; - - BOOST::uint8_t& access(); - void run_until( nes_time_t ); -}; -/* -struct namco_state_t -{ - BOOST::uint8_t regs [0x80]; - BOOST::uint8_t addr; - BOOST::uint8_t unused; - BOOST::uint8_t positions [8]; - BOOST::uint32_t delays [8]; -}; -*/ - -inline BOOST::uint8_t& Nes_Namco_Apu::access() -{ - int addr = addr_reg & 0x7f; - if ( addr_reg & 0x80 ) - addr_reg = (addr + 1) | 0x80; - return reg [addr]; -} - -inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } - -inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } - -inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; } - -inline int Nes_Namco_Apu::read_data() { return access(); } - -inline void Nes_Namco_Apu::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Namco_Apu::write_data( nes_time_t time, int data ) -{ - run_until( time ); - access() = data; -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Oscs.cpp b/quicknes/nes_emu/Nes_Oscs.cpp deleted file mode 100644 index 704274d01a..0000000000 --- a/quicknes/nes_emu/Nes_Oscs.cpp +++ /dev/null @@ -1,545 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -// Nes_Osc - -void Nes_Osc::clock_length( int halt_mask ) -{ - if ( length_counter && !(regs [0] & halt_mask) ) - length_counter--; -} - -void Nes_Envelope::clock_envelope() -{ - int period = regs [0] & 15; - if ( reg_written [3] ) { - reg_written [3] = false; - env_delay = period; - envelope = 15; - } - else if ( --env_delay < 0 ) { - env_delay = period; - if ( envelope | (regs [0] & 0x20) ) - envelope = (envelope - 1) & 15; - } -} - -int Nes_Envelope::volume() const -{ - return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; -} - -// Nes_Square - -void Nes_Square::clock_sweep( int negative_adjust ) -{ - int sweep = regs [1]; - - if ( --sweep_delay < 0 ) - { - reg_written [1] = true; - - int period = this->period(); - int shift = sweep & shift_mask; - if ( shift && (sweep & 0x80) && period >= 8 ) - { - int offset = period >> shift; - - if ( sweep & negate_flag ) - offset = negative_adjust - offset; - - if ( period + offset < 0x800 ) - { - period += offset; - // rewrite period - regs [2] = period & 0xff; - regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); - } - } - } - - if ( reg_written [1] ) { - reg_written [1] = false; - sweep_delay = (sweep >> 4) & 7; - } -} - -// TODO: clean up -inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ) -{ - long remain = end_time - time; - if ( remain > 0 ) - { - int count = (remain + timer_period - 1) / timer_period; - phase = (phase + count) & (phase_range - 1); - time += (long) count * timer_period; - } - return time; -} - -void Nes_Square::run( nes_time_t time, nes_time_t end_time ) -{ - const int period = this->period(); - const int timer_period = (period + 1) * 2; - - if ( !output ) - { - delay = maintain_phase( time + delay, end_time, timer_period ) - end_time; - return; - } - - int offset = period >> (regs [1] & shift_mask); - if ( regs [1] & negate_flag ) - offset = 0; - - const int volume = this->volume(); - if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) - { - if ( last_amp ) { - synth.offset( time, -last_amp, output ); - last_amp = 0; - } - - time += delay; - time = maintain_phase( time, end_time, timer_period ); - } - else - { - // handle duty select - int duty_select = (regs [0] >> 6) & 3; - int duty = 1 << duty_select; // 1, 2, 4, 2 - int amp = 0; - if ( duty_select == 3 ) { - duty = 2; // negated 25% - amp = volume; - } - if ( phase < duty ) - amp ^= volume; - - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - const Synth& synth = this->synth; - int delta = amp * 2 - volume; - int phase = this->phase; - - do { - phase = (phase + 1) & (phase_range - 1); - if ( phase == 0 || phase == duty ) { - delta = -delta; - synth.offset_inline( time, delta, output ); - } - time += timer_period; - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->phase = phase; - } - } - - delay = time - end_time; -} - -// Nes_Triangle - -void Nes_Triangle::clock_linear_counter() -{ - if ( reg_written [3] ) - linear_counter = regs [0] & 0x7f; - else if ( linear_counter ) - linear_counter--; - - if ( !(regs [0] & 0x80) ) - reg_written [3] = false; -} - -inline int Nes_Triangle::calc_amp() const -{ - int amp = phase_range - phase; - if ( amp < 0 ) - amp = phase - (phase_range + 1); - return amp; -} - -// TODO: clean up -inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ) -{ - long remain = end_time - time; - if ( remain > 0 ) - { - int count = (remain + timer_period - 1) / timer_period; - phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); - phase++; - time += (long) count * timer_period; - } - return time; -} - -void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) -{ - const int timer_period = period() + 1; - if ( !output ) - { - time += delay; - delay = 0; - if ( length_counter && linear_counter && timer_period >= 3 ) - delay = maintain_phase( time, end_time, timer_period ) - end_time; - return; - } - - // to do: track phase when period < 3 - // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. - - int delta = update_amp( calc_amp() ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) - { - time = end_time; - } - else if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - - int phase = this->phase; - int volume = 1; - if ( phase > phase_range ) { - phase -= phase_range; - volume = -volume; - } - - do { - if ( --phase == 0 ) { - phase = phase_range; - volume = -volume; - } - else { - synth.offset_inline( time, volume, output ); - } - - time += timer_period; - } - while ( time < end_time ); - - if ( volume < 0 ) - phase += phase_range; - this->phase = phase; - last_amp = calc_amp(); - } - delay = time - end_time; -} - -// Nes_Dmc - -void Nes_Dmc::reset() -{ - address = 0; - dac = 0; - buf = 0; - bits_remain = 1; - bits = 0; - buf_full = false; - silence = true; - next_irq = Nes_Apu::no_irq; - irq_flag = false; - irq_enabled = false; - - Nes_Osc::reset(); - period = 0x1ac; -} - -void Nes_Dmc::recalc_irq() -{ - nes_time_t irq = Nes_Apu::no_irq; - if ( irq_enabled && length_counter ) - irq = apu->last_dmc_time + delay + - ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; - if ( irq != next_irq ) { - next_irq = irq; - apu->irq_changed(); - } -} - -int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const -{ - if ( last_read ) - *last_read = time; - - if ( length_counter == 0 ) - return 0; // not reading - - long first_read = next_read_time(); - long avail = time - first_read; - if ( avail <= 0 ) - return 0; - - int count = (avail - 1) / (period * 8) + 1; - if ( !(regs [0] & loop_flag) && count > length_counter ) - count = length_counter; - - if ( last_read ) { - *last_read = first_read + (count - 1) * (period * 8) + 1; - assert( *last_read <= time ); - assert( count == count_reads( *last_read, NULL ) ); - assert( count - 1 == count_reads( *last_read - 1, NULL ) ); - } - - return count; -} - -static const short dmc_period_table [2] [16] = { - {0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC - 0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036}, - - {0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested) - 0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032} // to do: verify PAL periods -}; - -inline void Nes_Dmc::reload_sample() -{ - address = 0x4000 + regs [2] * 0x40; - length_counter = regs [3] * 0x10 + 1; -} - -static const unsigned char dac_table [128] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, - 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, - 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, - 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, - 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, - 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, - 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, - 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, -}; - -void Nes_Dmc::write_register( int addr, int data ) -{ - if ( addr == 0 ) - { - period = dmc_period_table [pal_mode] [data & 15]; - irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled - irq_flag &= irq_enabled; - recalc_irq(); - } - else if ( addr == 1 ) - { - int old_dac = dac; - dac = data & 0x7F; - - // adjust last_amp so that "pop" amplitude will be properly non-linear - // with respect to change in dac - int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); - if ( !nonlinear ) - last_amp = faked_nonlinear; - } -} - -void Nes_Dmc::start() -{ - reload_sample(); - fill_buffer(); - recalc_irq(); -} - -void Nes_Dmc::fill_buffer() -{ - if ( !buf_full && length_counter ) - { - require( prg_reader ); // prg_reader must be set - buf = prg_reader( prg_reader_data, 0x8000u + address ); - address = (address + 1) & 0x7FFF; - buf_full = true; - if ( --length_counter == 0 ) - { - if ( regs [0] & loop_flag ) { - reload_sample(); - } - else { - apu->osc_enables &= ~0x10; - irq_flag = irq_enabled; - next_irq = Nes_Apu::no_irq; - apu->irq_changed(); - } - } - } -} - -void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) -{ - int delta = update_amp( dac ); - if ( !output ) - silence = true; - else if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - int bits_remain = this->bits_remain; - if ( silence && !buf_full ) - { - int count = (end_time - time + period - 1) / period; - bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; - time += count * period; - } - else - { - Blip_Buffer* const output = this->output; - const int period = this->period; - int bits = this->bits; - int dac = this->dac; - - do - { - if ( !silence ) - { - int step = (bits & 1) * 4 - 2; - bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) { - dac += step; - synth.offset_inline( time, step, output ); - } - } - - time += period; - - if ( --bits_remain == 0 ) - { - bits_remain = 8; - if ( !buf_full ) { - silence = true; - } - else { - silence = false; - bits = buf; - buf_full = false; - if ( !output ) - silence = true; - fill_buffer(); - } - } - } - while ( time < end_time ); - - this->dac = dac; - this->last_amp = dac; - this->bits = bits; - } - this->bits_remain = bits_remain; - } - delay = time - end_time; -} - -// Nes_Noise - -static const short noise_period_table [16] = { - 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, - 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 -}; - -void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) -{ - int period = noise_period_table [regs [2] & 15]; - #if NES_APU_NOISE_LOW_CPU - if ( period < 8 ) - { - period = 8; - } - #endif - - if ( !output ) - { - // TODO: clean up - time += delay; - delay = time + (end_time - time + period - 1) / period * period - end_time; - return; - } - - const int volume = this->volume(); - int amp = (noise & 1) ? volume : 0; - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - const int mode_flag = 0x80; - - if ( !volume ) - { - // round to next multiple of period - time += (end_time - time + period - 1) / period * period; - - // approximate noise cycling while muted, by shuffling up noise register - // to do: precise muted noise cycling? - if ( !(regs [2] & mode_flag) ) { - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - else - { - Blip_Buffer* const output = this->output; - - // using resampled time avoids conversion in synth.offset() - blip_resampled_time_t rperiod = output->resampled_duration( period ); - blip_resampled_time_t rtime = output->resampled_time( time ); - - int noise = this->noise; - int delta = amp * 2 - volume; - const int tap = (regs [2] & mode_flag ? 8 : 13); - - do { - int feedback = (noise << tap) ^ (noise << 14); - time += period; - - if ( (noise + 1) & 2 ) { - // bits 0 and 1 of noise differ - delta = -delta; - synth.offset_resampled( rtime, delta, output ); - } - - rtime += rperiod; - noise = (feedback & 0x4000) | (noise >> 1); - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->noise = noise; - } - } - - delay = time - end_time; -} - diff --git a/quicknes/nes_emu/Nes_Oscs.h b/quicknes/nes_emu/Nes_Oscs.h deleted file mode 100644 index ee14ac7a37..0000000000 --- a/quicknes/nes_emu/Nes_Oscs.h +++ /dev/null @@ -1,150 +0,0 @@ - -// Private oscillators used by Nes_Apu - -// Nes_Snd_Emu 0.1.7 - -#ifndef NES_OSCS_H -#define NES_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Nes_Apu; - -struct Nes_Osc -{ - unsigned char regs [4]; - bool reg_written [4]; - Blip_Buffer* output; - int length_counter;// length counter (0 if unused by oscillator) - int delay; // delay until next (potential) transition - int last_amp; // last amplitude oscillator was outputting - - void clock_length( int halt_mask ); - int period() const { - return (regs [3] & 7) * 0x100 + (regs [2] & 0xff); - } - void reset() { - delay = 0; - last_amp = 0; - } - int update_amp( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - return delta; - } -}; - -struct Nes_Envelope : Nes_Osc -{ - int envelope; - int env_delay; - - void clock_envelope(); - int volume() const; - void reset() { - envelope = 0; - env_delay = 0; - Nes_Osc::reset(); - } -}; - -// Nes_Square -struct Nes_Square : Nes_Envelope -{ - enum { negate_flag = 0x08 }; - enum { shift_mask = 0x07 }; - enum { phase_range = 8 }; - int phase; - int sweep_delay; - - typedef Blip_Synth Synth; - Synth const& synth; // shared between squares - - Nes_Square( Synth const* s ) : synth( *s ) { } - - void clock_sweep( int adjust ); - void run( nes_time_t, nes_time_t ); - void reset() { - sweep_delay = 0; - Nes_Envelope::reset(); - } - nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ); -}; - -// Nes_Triangle -struct Nes_Triangle : Nes_Osc -{ - enum { phase_range = 16 }; - int phase; - int linear_counter; - Blip_Synth synth; - - int calc_amp() const; - void run( nes_time_t, nes_time_t ); - void clock_linear_counter(); - void reset() { - linear_counter = 0; - phase = 1; - Nes_Osc::reset(); - } - nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ); -}; - -// Nes_Noise -struct Nes_Noise : Nes_Envelope -{ - int noise; - Blip_Synth synth; - - void run( nes_time_t, nes_time_t ); - void reset() { - noise = 1 << 14; - Nes_Envelope::reset(); - } -}; - -// Nes_Dmc -struct Nes_Dmc : Nes_Osc -{ - int address; // address of next byte to read - int period; - //int length_counter; // bytes remaining to play (already defined in Nes_Osc) - int buf; - int bits_remain; - int bits; - bool buf_full; - bool silence; - - enum { loop_flag = 0x40 }; - - int dac; - - nes_time_t next_irq; - bool irq_enabled; - bool irq_flag; - bool pal_mode; - bool nonlinear; - - int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function - void* prg_reader_data; - - Nes_Apu* apu; - - Blip_Synth synth; - - void start(); - void write_register( int, int ); - void run( nes_time_t, nes_time_t ); - void recalc_irq(); - void fill_buffer(); - void reload_sample(); - void reset(); - int count_reads( nes_time_t, nes_time_t* ) const; - nes_time_t next_read_time() const; -}; - -#endif - diff --git a/quicknes/nes_emu/Nes_Ppu.cpp b/quicknes/nes_emu/Nes_Ppu.cpp deleted file mode 100644 index a3ff00aad6..0000000000 --- a/quicknes/nes_emu/Nes_Ppu.cpp +++ /dev/null @@ -1,669 +0,0 @@ - -// Timing and behavior of PPU - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Ppu.h" - -#include -#include "Nes_State.h" -#include "Nes_Mapper.h" -#include "Nes_Core.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -// to do: remove unnecessary run_until() calls - -#include "blargg_source.h" - -// Timing - -ppu_time_t const scanline_len = Nes_Ppu::scanline_len; - -// if non-zero, report sprite max at fixed time rather than calculating it -nes_time_t const fixed_sprite_max_time = 0; // 1 * ((21 + 164) * scanline_len + 100) / ppu_overclock; -int const sprite_max_cpu_offset = 2420 + 3; - -ppu_time_t const t_to_v_time = 20 * scanline_len + 302; -ppu_time_t const even_odd_time = 20 * scanline_len + 328; - -ppu_time_t const first_scanline_time = 21 * scanline_len + 60; // this can be varied -ppu_time_t const first_hblank_time = 21 * scanline_len + 252; - -ppu_time_t const earliest_sprite_max = sprite_max_cpu_offset * ppu_overclock; -ppu_time_t const earliest_sprite_hit = 21 * scanline_len + 339; // needs to be 22 * scanline_len when fixed_sprite_max_time is set - -nes_time_t const vbl_end_time = 2272; -ppu_time_t const max_frame_length = 262 * scanline_len; -//ppu_time_t const max_frame_length = 320 * scanline_len; // longer frame for testing movie resync -nes_time_t const earliest_vbl_end_time = max_frame_length / ppu_overclock - 10; - -// Scanline rendering - -void Nes_Ppu::render_bg_until_( nes_time_t cpu_time ) -{ - ppu_time_t time = ppu_time( cpu_time ); - ppu_time_t const frame_duration = scanline_len * 261; - if ( time > frame_duration ) - time = frame_duration; - - // one-time events - if ( frame_phase <= 1 ) - { - if ( frame_phase < 1 ) - { - // vtemp->vaddr - frame_phase = 1; - if ( w2001 & 0x08 ) - vram_addr = vram_temp; - } - - // variable-length scanline - if ( time <= even_odd_time ) - { - next_bg_time = nes_time( even_odd_time ); - return; - } - frame_phase = 2; - if ( !(w2001 & 0x08) || emu.nes.frame_count & 1 ) - { - if ( --frame_length_extra < 0 ) - { - frame_length_extra = 2; - frame_length_++; - } - burst_phase--; - } - burst_phase = (burst_phase + 2) % 3; - } - - // scanlines - if ( scanline_time < time ) - { - int count = (time - scanline_time + scanline_len) / scanline_len; - - // hblank before next scanline - if ( hblank_time < scanline_time ) - { - hblank_time += scanline_len; - run_hblank( 1 ); - } - - scanline_time += count * scanline_len; - - hblank_time += scanline_len * (count - 1); - int saved_vaddr = vram_addr; - - int start = scanline_count; - scanline_count += count; - draw_background( start, count ); - - vram_addr = saved_vaddr; // to do: this is cheap - run_hblank( count - 1 ); - } - - // hblank after current scanline - ppu_time_t next_ppu_time = hblank_time; - if ( hblank_time < time ) - { - hblank_time += scanline_len; - run_hblank( 1 ); - next_ppu_time = scanline_time; // scanline will run next - } - assert( time <= hblank_time ); - - // either hblank or scanline comes next - next_bg_time = nes_time( next_ppu_time ); -} - -void Nes_Ppu::render_until_( nes_time_t time ) -{ - // render bg scanlines then render sprite scanlines up to wherever bg was rendered to - - render_bg_until( time ); - next_sprites_time = nes_time( scanline_time ); - if ( host_pixels ) - { - int start = next_sprites_scanline; - int count = scanline_count - start; - if ( count > 0 ) - { - next_sprites_scanline += count; - draw_sprites( start, count ); - } - } -} - -// Frame events - -inline void Nes_Ppu::end_vblank() -{ - // clear VBL, sprite hit, and max sprites flags first time after 20 scanlines - r2002 &= end_vbl_mask; - end_vbl_mask = ~0; -} - -inline void Nes_Ppu::run_end_frame( nes_time_t time ) -{ - if ( !frame_ended ) - { - // update frame_length - render_bg_until( time ); - - // set VBL when end of frame is reached - nes_time_t len = frame_length(); - if ( time >= len ) - { - r2002 |= 0x80; - frame_ended = true; - if ( w2000 & 0x80 ) - nmi_time_ = len + 2 - (frame_length_extra >> 1); - } - } -} - -// Sprite max - -inline void Nes_Ppu::invalidate_sprite_max_() -{ - next_sprite_max_run = earliest_sprite_max / ppu_overclock; - sprite_max_set_time = 0; -} - -void Nes_Ppu::run_sprite_max_( nes_time_t cpu_time ) -{ - end_vblank(); // might get run outside $2002 handler - - // 577.0 / 0x10000 ~= 1.0 / 113.581, close enough to accurately calculate which scanline it is - int start_scanline = next_sprite_max_scanline; - next_sprite_max_scanline = unsigned ((cpu_time - sprite_max_cpu_offset) * 577) / 0x10000u; - assert( next_sprite_max_scanline >= 0 && next_sprite_max_scanline <= last_sprite_max_scanline ); - - if ( !sprite_max_set_time ) - { - if ( !(w2001 & 0x18) ) - return; - - long t = recalc_sprite_max( start_scanline ); - sprite_max_set_time = indefinite_time; - if ( t > 0 ) - sprite_max_set_time = t / 3 + sprite_max_cpu_offset; - next_sprite_max_run = sprite_max_set_time; - //dprintf( "sprite_max_set_time: %d\n", sprite_max_set_time ); - } - - if ( cpu_time > sprite_max_set_time ) - { - r2002 |= 0x20; - //dprintf( "Sprite max flag set: %d\n", sprite_max_set_time ); - next_sprite_max_run = indefinite_time; - } -} - -inline void Nes_Ppu::run_sprite_max( nes_time_t t ) -{ - if ( !fixed_sprite_max_time && t > next_sprite_max_run ) - run_sprite_max_( t ); -} - -inline void Nes_Ppu::invalidate_sprite_max( nes_time_t t ) -{ - if ( !fixed_sprite_max_time && !(r2002 & 0x20) ) - { - run_sprite_max( t ); - invalidate_sprite_max_(); - } -} - -// Sprite 0 hit - -inline int Nes_Ppu_Impl::first_opaque_sprite_line() /*const*/ -{ - // advance earliest time if sprite has blank lines at beginning - byte const* p = map_chr( sprite_tile_index( spr_ram ) * 16 ); - int twice = w2000 >> 5 & 1; // loop twice if double height is set - int line = 0; - do - { - for ( int n = 8; n--; p++ ) - { - if ( p [0] | p [8] ) - return line; - line++; - } - - p += 8; - } - while ( !--twice ); - return line; -} - -void Nes_Ppu::update_sprite_hit( nes_time_t cpu_time ) -{ - ppu_time_t earliest = earliest_sprite_hit + spr_ram [0] * scanline_len + spr_ram [3]; - //ppu_time_t latest = earliest + sprite_height() * scanline_len; - - earliest += first_opaque_sprite_line() * scanline_len; - - ppu_time_t time = ppu_time( cpu_time ); - next_sprite_hit_check = indefinite_time; - - if ( false ) - if ( earliest < time ) - { - r2002 |= 0x40; - return; - } - - if ( time < earliest ) - { - next_sprite_hit_check = nes_time( earliest ); - return; - } - - // within possible range; render scanline and compare pixels - int count_needed = 2 + (time - earliest_sprite_hit - spr_ram [3]) / scanline_len; - if ( count_needed > 240 ) - count_needed = 240; - while ( scanline_count < count_needed ) - render_bg_until( max( cpu_time, next_bg_time + 1 ) ); - - if ( sprite_hit_found < 0 ) - return; // sprite won't hit - - if ( !sprite_hit_found ) - { - // check again next scanline - next_sprite_hit_check = nes_time( earliest_sprite_hit + spr_ram [3] + - (scanline_count - 1) * scanline_len ); - } - else - { - // hit found - ppu_time_t hit_time = earliest_sprite_hit + sprite_hit_found - scanline_len; - - if ( time < hit_time ) - { - next_sprite_hit_check = nes_time( hit_time ); - return; - } - - //dprintf( "Sprite hit x: %d, y: %d, scanline_count: %d\n", - // sprite_hit_found % 341, sprite_hit_found / 341, scanline_count ); - - r2002 |= 0x40; - } -} - -// $2002 - -inline void Nes_Ppu::query_until( nes_time_t time ) -{ - end_vblank(); - - // sprite hit - if ( time > next_sprite_hit_check ) - update_sprite_hit( time ); - - // sprite max - if ( !fixed_sprite_max_time ) - run_sprite_max( time ); - else if ( time >= fixed_sprite_max_time ) - r2002 |= (w2001 << 1 & 0x20) | (w2001 << 2 & 0x20); -} - -int Nes_Ppu::read_2002( nes_time_t time ) -{ - nes_time_t next = next_status_event; - next_status_event = vbl_end_time; - int extra_clock = extra_clocks ? (extra_clocks - 1) >> 2 & 1 : 0; - if ( time > next && time > vbl_end_time + extra_clock ) - { - query_until( time ); - - next_status_event = next_sprite_hit_check; - nes_time_t const next_max = fixed_sprite_max_time ? - fixed_sprite_max_time : next_sprite_max_run; - if ( next_status_event > next_max ) - next_status_event = next_max; - - if ( time > earliest_open_bus_decay() ) - { - next_status_event = earliest_open_bus_decay(); - update_open_bus( time ); - } - - if ( time > earliest_vbl_end_time ) - { - if ( next_status_event > earliest_vbl_end_time ) - next_status_event = earliest_vbl_end_time; - run_end_frame( time ); - - // special vbl behavior when read is just before or at clock when it's set - if ( extra_clocks != 1 ) - { - if ( time == frame_length() ) - { - nmi_time_ = indefinite_time; - //dprintf( "Suppressed NMI\n" ); - } - } - else if ( time == frame_length() - 1 ) - { - r2002 &= ~0x80; - frame_ended = true; - nmi_time_ = indefinite_time; - //dprintf( "Suppressed NMI\n" ); - } - } - } - emu.set_ppu_2002_time( next_status_event ); - - int result = r2002; - second_write = false; - r2002 = result & ~0x80; - poke_open_bus( time, result, 0xE0 ); - update_open_bus( time ); - return ( result & 0xE0 ) | ( open_bus & 0x1F ); -} - -void Nes_Ppu::dma_sprites( nes_time_t time, void const* in ) -{ - //dprintf( "%d sprites written\n", time ); - render_until( time ); - - invalidate_sprite_max( time ); - // catch anything trying to dma while rendering is enabled - check( time + 513 <= vbl_end_time || !(w2001 & 0x18) ); - - memcpy( spr_ram + w2003, in, 0x100 - w2003 ); - memcpy( spr_ram, (char*) in + 0x100 - w2003, w2003 ); -} - -// Read - -inline int Nes_Ppu_Impl::read_2007( int addr ) -{ - int result = r2007; - if ( addr < 0x2000 ) - { - r2007 = *map_chr( addr ); - } - else - { - r2007 = get_nametable( addr ) [addr & 0x3ff]; - if ( addr >= 0x3f00 ) - { - return palette [map_palette( addr )] | ( open_bus & 0xC0 ); - } - } - return result; -} - -int Nes_Ppu::read( unsigned addr, nes_time_t time ) -{ - if ( addr & ~0x2007 ) - dprintf( "Read from mirrored PPU register 0x%04X\n", addr ); - - switch ( addr & 7 ) - { - // status - case 2: // handled inline - return read_2002( time ); - - // sprite ram - case 4: { - int result = spr_ram [w2003]; - if ( (w2003 & 3) == 2 ) - result &= 0xe3; - poke_open_bus( time, result, ~0 ); - return result; - } - - // video ram - case 7: { - render_bg_until( time ); - int addr = vram_addr; - int new_addr = addr + addr_inc; - vram_addr = new_addr; - if ( ~addr & new_addr & vaddr_clock_mask ) - { - emu.mapper->a12_clocked(); - addr = vram_addr - addr_inc; // avoid having to save across func call - } - int result = read_2007( addr & 0x3fff ); - poke_open_bus( time, result, ( ( addr & 0x3fff ) >= 0x3f00 ) ? 0x3F : ~0 ); - return result; - } - - default: - dprintf( "Read from unimplemented PPU register 0x%04X\n", addr ); - break; - } - - update_open_bus( time ); - - return open_bus; -} - -// Write - -void Nes_Ppu::write( nes_time_t time, unsigned addr, int data ) -{ - if ( addr & ~0x2007 ) - dprintf( "Wrote to mirrored PPU register 0x%04X\n", addr ); - - switch ( addr & 7 ) - { - case 0:{// control - int changed = w2000 ^ data; - - if ( changed & 0x28 ) - render_until( time ); // obj height or pattern addr changed - else if ( changed & 0x10 ) - render_bg_until( time ); // bg pattern addr changed - else if ( ((data << 10) ^ vram_temp) & 0x0C00 ) - render_bg_until( time ); // nametable changed - - if ( changed & 0x80 ) - { - if ( time > vbl_end_time + ((extra_clocks - 1) >> 2 & 1) ) - end_vblank(); // to do: clean this up - - if ( data & 0x80 & r2002 ) - { - nmi_time_ = time + 2; - emu.event_changed(); - } - if ( time >= earliest_vbl_end_time ) - run_end_frame( time - 1 + (extra_clocks & 1) ); - } - - // nametable select - vram_temp = (vram_temp & ~0x0C00) | ((data & 3) * 0x400); - - if ( changed & 0x20 ) // sprite height changed - invalidate_sprite_max( time ); - w2000 = data; - addr_inc = data & 4 ? 32 : 1; - - break; - } - - case 1:{// sprites, bg enable - int changed = w2001 ^ data; - - if ( changed & 0xE1 ) - { - render_until( time + 1 ); // emphasis/monochrome bits changed - palette_changed = 0x18; - } - - if ( changed & 0x14 ) - render_until( time + 1 ); // sprite enable/clipping changed - else if ( changed & 0x0A ) - render_bg_until( time + 1 ); // bg enable/clipping changed - - if ( changed & 0x08 ) // bg enabled changed - emu.mapper->run_until( time ); - - if ( !(w2001 & 0x18) != !(data & 0x18) ) - invalidate_sprite_max( time ); // all rendering just turned on or off - - w2001 = data; - - if ( changed & 0x08 ) - emu.irq_changed(); - - break; - } - - case 3: // spr addr - w2003 = data; - poke_open_bus( time, w2003, ~0 ); - break; - - case 4: - //dprintf( "%d sprites written\n", time ); - if ( time > first_scanline_time / ppu_overclock ) - { - render_until( time ); - invalidate_sprite_max( time ); - } - spr_ram [w2003] = data; - w2003 = (w2003 + 1) & 0xff; - break; - - case 5: - render_bg_until( time ); - if ( (second_write ^= 1) ) - { - pixel_x = data & 7; - vram_temp = (vram_temp & ~0x1f) | (data >> 3); - } - else - { - vram_temp = (vram_temp & ~0x73e0) | - (data << 12 & 0x7000) | (data << 2 & 0x03e0); - } - break; - - case 6: - render_bg_until( time ); - if ( (second_write ^= 1) ) - { - vram_temp = (vram_temp & 0xff) | (data << 8 & 0x3f00); - } - else - { - int changed = ~vram_addr & vram_temp; - vram_addr = vram_temp = (vram_temp & 0xff00) | data; - if ( changed & vaddr_clock_mask ) - emu.mapper->a12_clocked(); - } - break; - - default: - dprintf( "Wrote to unimplemented PPU register 0x%04X\n", addr ); - break; - } - - poke_open_bus( time, data, ~0 ); -} - -// Frame begin/end - -nes_time_t Nes_Ppu::begin_frame( ppu_time_t timestamp ) -{ - // current time - int cpu_timestamp = timestamp / ppu_overclock; - extra_clocks = timestamp - cpu_timestamp * ppu_overclock; - - // frame end - ppu_time_t const frame_end = max_frame_length - 1 - extra_clocks; - frame_length_ = (frame_end + (ppu_overclock - 1)) / ppu_overclock; - frame_length_extra = frame_length_ * ppu_overclock - frame_end; - assert( (unsigned) frame_length_extra < 3 ); - - // nmi - nmi_time_ = indefinite_time; - if ( w2000 & 0x80 & r2002 ) - nmi_time_ = 2 - (extra_clocks >> 1); - - // bg rendering - frame_phase = 0; - scanline_count = 0; - hblank_time = first_hblank_time; - scanline_time = first_scanline_time; - next_bg_time = nes_time( t_to_v_time ); - - // sprite rendering - next_sprites_scanline = 0; - next_sprites_time = 0; - - // status register - frame_ended = false; - end_vbl_mask = ~0xE0; - next_status_event = 0; - sprite_hit_found = 0; - next_sprite_hit_check = 0; - next_sprite_max_scanline = 0; - invalidate_sprite_max_(); - - decay_low += cpu_timestamp; - decay_high += cpu_timestamp; - - base::begin_frame(); - - //dprintf( "cpu_timestamp: %d\n", cpu_timestamp ); - return cpu_timestamp; -} - -ppu_time_t Nes_Ppu::end_frame( nes_time_t end_time ) -{ - render_bg_until( end_time ); - render_until( end_time ); - query_until( end_time ); - run_end_frame( end_time ); - - update_open_bus( end_time ); - decay_low -= end_time; - decay_high -= end_time; - - // to do: do more PPU RE to get exact behavior - if ( w2001 & 0x08 ) - { - unsigned a = vram_addr + 2; - if ( (vram_addr & 0xff) >= 0xfe ) - a = (vram_addr ^ 0x400) - 0x1e; - vram_addr = a; - } - - if ( w2001 & 0x10 ) - w2003 = 0; - - suspend_rendering(); - - return (end_time - frame_length_) * ppu_overclock + frame_length_extra; -} - -void Nes_Ppu::poke_open_bus( nes_time_t time, int data, int mask ) -{ - open_bus = ( open_bus & ~mask ) | ( data & mask ); - if ( mask & 0x1F ) decay_low = time + scanline_len * 100 / ppu_overclock; - if ( mask & 0xE0 ) decay_high = time + scanline_len * 100 / ppu_overclock; -} - -const nes_time_t Nes_Ppu::earliest_open_bus_decay() const -{ - return ( decay_low < decay_high ) ? decay_low : decay_high; -} diff --git a/quicknes/nes_emu/Nes_Ppu.h b/quicknes/nes_emu/Nes_Ppu.h deleted file mode 100644 index 905c23ce00..0000000000 --- a/quicknes/nes_emu/Nes_Ppu.h +++ /dev/null @@ -1,141 +0,0 @@ - -// NES PPU emulator - -// Nes_Emu 0.7.0 - -#ifndef NES_PPU_H -#define NES_PPU_H - -#include "Nes_Ppu_Rendering.h" -class Nes_Mapper; -class Nes_Core; - -typedef long nes_time_t; -typedef long ppu_time_t; // ppu_time_t = nes_time_t * ppu_overclock - -ppu_time_t const ppu_overclock = 3; // PPU clocks for each CPU clock - -class Nes_Ppu : public Nes_Ppu_Rendering { - typedef Nes_Ppu_Rendering base; -public: - Nes_Ppu( Nes_Core* ); - - // Begin PPU frame and return beginning CPU timestamp - nes_time_t begin_frame( ppu_time_t ); - - nes_time_t nmi_time() { return nmi_time_; } - void acknowledge_nmi() { nmi_time_ = LONG_MAX / 2 + 1; } - - int read_2002( nes_time_t ); - int read( unsigned addr, nes_time_t ); - void write( nes_time_t, unsigned addr, int ); - - void render_bg_until( nes_time_t ); - void render_until( nes_time_t ); - - // CPU time that frame will have ended by - int frame_length() const { return frame_length_; } - - // End frame rendering and return PPU timestamp for next frame - ppu_time_t end_frame( nes_time_t ); - - // Do direct memory copy to sprite RAM - void dma_sprites( nes_time_t, void const* in ); - - int burst_phase; - -private: - - Nes_Core& emu; - - enum { indefinite_time = LONG_MAX / 2 + 1 }; - - void suspend_rendering(); - int read_( unsigned addr, nes_time_t ); // note swapped arguments! - - // NES<->PPU time conversion - int extra_clocks; - ppu_time_t ppu_time( nes_time_t t ) const { return t * ppu_overclock + extra_clocks; } - nes_time_t nes_time( ppu_time_t t ) const { return (t - extra_clocks) / ppu_overclock; } - - // frame - nes_time_t nmi_time_; - int end_vbl_mask; - int frame_length_; - int frame_length_extra; - bool frame_ended; - void end_vblank(); - void run_end_frame( nes_time_t ); - - // bg rendering - nes_time_t next_bg_time; - ppu_time_t scanline_time; - ppu_time_t hblank_time; - int scanline_count; - int frame_phase; - void render_bg_until_( nes_time_t ); - void run_scanlines( int count ); - - // sprite rendering - ppu_time_t next_sprites_time; - int next_sprites_scanline; - void render_until_( nes_time_t ); - - // $2002 status register - nes_time_t next_status_event; - void query_until( nes_time_t ); - - // sprite hit - nes_time_t next_sprite_hit_check; - void update_sprite_hit( nes_time_t ); - - // open bus decay - void update_open_bus( nes_time_t ); - void poke_open_bus( nes_time_t, int, int mask ); - const nes_time_t earliest_open_bus_decay() const; - - // sprite max - nes_time_t next_sprite_max_run; // doesn't need to run until this time - nes_time_t sprite_max_set_time; // if 0, needs to be recalculated - int next_sprite_max_scanline; - void run_sprite_max_( nes_time_t ); - void run_sprite_max( nes_time_t ); - void invalidate_sprite_max_(); - void invalidate_sprite_max( nes_time_t ); - - friend int nes_cpu_read_likely_ppu( class Nes_Core*, unsigned, nes_time_t ); -}; - -inline void Nes_Ppu::suspend_rendering() -{ - next_bg_time = indefinite_time; - next_sprites_time = indefinite_time; - extra_clocks = 0; -} - -inline Nes_Ppu::Nes_Ppu( Nes_Core* e ) : emu( *e ) -{ - burst_phase = 0; - suspend_rendering(); -} - -inline void Nes_Ppu::render_until( nes_time_t t ) -{ - if ( t > next_sprites_time ) - render_until_( t ); -} - -inline void Nes_Ppu::render_bg_until( nes_time_t t ) -{ - if ( t > next_bg_time ) - render_bg_until_( t ); -} - -inline void Nes_Ppu::update_open_bus( nes_time_t time ) -{ - if ( time >= decay_low ) open_bus &= ~0x1F; - if ( time >= decay_high ) open_bus &= ~0xE0; -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Ppu_Bg.h b/quicknes/nes_emu/Nes_Ppu_Bg.h deleted file mode 100644 index e1ca5f2fbf..0000000000 --- a/quicknes/nes_emu/Nes_Ppu_Bg.h +++ /dev/null @@ -1,68 +0,0 @@ -while ( true ) -{ - while ( count-- ) - { - int attrib = attr_table [addr >> 2 & 0x07]; - attrib >>= (addr >> 4 & 4) | (addr & 2); - unsigned long offset = (attrib & 3) * attrib_factor + this->palette_offset; - - // draw one tile - cache_t const* lines = this->get_bg_tile( nametable [addr] + bg_bank ); - byte* p = pixels; - addr++; - pixels += 8; // next tile - - if ( !clipped ) - { - // optimal case: no clipping - for ( int n = 4; n--; ) - { - unsigned long line = *lines++; - ((uint32_t*) p) [0] = (line >> 4 & mask) + offset; - ((uint32_t*) p) [1] = (line & mask) + offset; - p += row_bytes; - ((uint32_t*) p) [0] = (line >> 6 & mask) + offset; - ((uint32_t*) p) [1] = (line >> 2 & mask) + offset; - p += row_bytes; - } - } - else - { - lines += fine_y >> 1; - - if ( fine_y & 1 ) - { - unsigned long line = *lines++; - ((uint32_t*) p) [0] = (line >> 6 & mask) + offset; - ((uint32_t*) p) [1] = (line >> 2 & mask) + offset; - p += row_bytes; - } - - for ( int n = height >> 1; n--; ) - { - unsigned long line = *lines++; - ((uint32_t*) p) [0] = (line >> 4 & mask) + offset; - ((uint32_t*) p) [1] = (line & mask) + offset; - p += row_bytes; - ((uint32_t*) p) [0] = (line >> 6 & mask) + offset; - ((uint32_t*) p) [1] = (line >> 2 & mask) + offset; - p += row_bytes; - } - - if ( height & 1 ) - { - unsigned long line = *lines; - ((uint32_t*) p) [0] = (line >> 4 & mask) + offset; - ((uint32_t*) p) [1] = (line & mask) + offset; - } - } - } - - count = count2; - count2 = 0; - addr -= 32; - attr_table = attr_table - nametable + nametable2; - nametable = nametable2; - if ( !count ) - break; -} diff --git a/quicknes/nes_emu/Nes_Ppu_Impl.cpp b/quicknes/nes_emu/Nes_Ppu_Impl.cpp deleted file mode 100644 index f46524bd4b..0000000000 --- a/quicknes/nes_emu/Nes_Ppu_Impl.cpp +++ /dev/null @@ -1,520 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Ppu_Impl.h" - -#include -#include "blargg_endian.h" -#include "Nes_State.h" -#include - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -int const cache_line_size = 128; // tile cache is kept aligned to this boundary - -Nes_Ppu_Impl::Nes_Ppu_Impl() -{ - impl = NULL; - chr_data = NULL; - chr_size = 0; - tile_cache = NULL; - host_palette = NULL; - max_palette_size = 0; - tile_cache_mem = NULL; - ppu_state_t::unused = 0; - - mmc24_enabled = false; - mmc24_latched[0] = 0; - mmc24_latched[1] = 0; - - #ifndef NDEBUG - // verify that unaligned accesses work - static unsigned char b [19] = { 0 }; - static unsigned char b2 [19] = { 1,2,3,4,0,5,6,7,8,0,9,0,1,2,0,3,4,5,6 }; - for ( int i = 0; i < 19; i += 5 ) - *(volatile BOOST::uint32_t*) &b [i] = *(volatile BOOST::uint32_t*) &b2 [i]; - assert( !memcmp( b, b2, 19 ) ); - #endif -} - -Nes_Ppu_Impl::~Nes_Ppu_Impl() -{ - close_chr(); - delete impl; -} - -int Nes_Ppu_Impl::peekaddr(int addr) -{ - if (addr < 0x2000) - return chr_data[map_chr_addr_peek(addr)]; - else - return get_nametable(addr)[addr & 0x3ff]; -} - - - -void Nes_Ppu_Impl::all_tiles_modified() -{ - any_tiles_modified = true; - memset( modified_tiles, ~0, sizeof modified_tiles ); -} - -blargg_err_t Nes_Ppu_Impl::open_chr( byte const* new_chr, long chr_data_size ) -{ - close_chr(); - - if ( !impl ) - { - impl = BLARGG_NEW impl_t; - CHECK_ALLOC( impl ); - chr_ram = impl->chr_ram; - } - - chr_data = new_chr; - chr_size = chr_data_size; - chr_is_writable = false; - - if ( chr_data_size == 0 ) - { - // CHR RAM - chr_data = impl->chr_ram; - chr_size = sizeof impl->chr_ram; - chr_is_writable = true; - } - - // allocate aligned memory for cache - assert( chr_size % chr_addr_size == 0 ); - long tile_count = chr_size / bytes_per_tile; - tile_cache_mem = BLARGG_NEW byte [tile_count * sizeof (cached_tile_t) * 2 + cache_line_size]; - CHECK_ALLOC( tile_cache_mem ); - tile_cache = (cached_tile_t*) (tile_cache_mem + cache_line_size - - (uintptr_t) tile_cache_mem % cache_line_size); - flipped_tiles = tile_cache + tile_count; - - // rebuild cache - all_tiles_modified(); - if ( !chr_is_writable ) - { - any_tiles_modified = false; - rebuild_chr( 0, chr_size ); - } - - return 0; -} - -void Nes_Ppu_Impl::close_chr() -{ - delete [] tile_cache_mem; - tile_cache_mem = NULL; -} - -void Nes_Ppu_Impl::set_chr_bank( int addr, int size, long data ) -{ - check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched? - //dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data ); - - if ( data + size > chr_size ) - data %= chr_size; - - int count = (unsigned) size / chr_page_size; - assert( chr_page_size * count == size ); - assert( addr + size <= chr_addr_size ); - - int page = (unsigned) addr / chr_page_size; - while ( count-- ) - { - chr_pages [page] = data - page * chr_page_size; - page++; - data += chr_page_size; - } -} - -void Nes_Ppu_Impl::set_chr_bank_ex( int addr, int size, long data ) -{ - mmc24_enabled = true; - - check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched? - //dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data ); - - if ( data + size > chr_size ) - data %= chr_size; - - int count = (unsigned) size / chr_page_size; - assert( chr_page_size * count == size ); - assert( addr + size <= chr_addr_size ); - - int page = (unsigned) addr / chr_page_size; - while ( count-- ) - { - chr_pages_ex [page] = data - page * chr_page_size; - page++; - data += chr_page_size; - } -} - -void Nes_Ppu_Impl::save_state( Nes_State_* out ) const -{ - *out->ppu = *this; - out->ppu_valid = true; - - memcpy( out->spr_ram, spr_ram, out->spr_ram_size ); - out->spr_ram_valid = true; - - out->nametable_size = 0x800; - memcpy( out->nametable, impl->nt_ram, 0x800 ); - if ( nt_banks [3] >= &impl->nt_ram [0xC00] ) - { - // save extra nametable data in chr - out->nametable_size = 0x1000; - memcpy( out->chr, &impl->nt_ram [0x800], 0x800 ); - } - - out->chr_size = 0; - if ( chr_is_writable ) - { - out->chr_size = chr_size; - check( out->nametable_size <= 0x800 ); - assert( out->nametable_size <= 0x800 ); - assert( out->chr_size <= out->chr_max ); - memcpy( out->chr, impl->chr_ram, out->chr_size ); - } -} - -void Nes_Ppu_Impl::load_state( Nes_State_ const& in ) -{ - set_nt_banks( 0, 0, 0, 0 ); - set_chr_bank( 0, 0x2000, 0 ); - - if ( in.ppu_valid ) - STATIC_CAST(ppu_state_t&,*this) = *in.ppu; - - if ( in.spr_ram_valid ) - memcpy( spr_ram, in.spr_ram, sizeof spr_ram ); - - assert( in.nametable_size <= (int) sizeof impl->nt_ram ); - if ( in.nametable_size >= 0x800 ) - { - if ( in.nametable_size > 0x800 ) - memcpy( &impl->nt_ram [0x800], in.chr, 0x800 ); - memcpy( impl->nt_ram, in.nametable, 0x800 ); - } - - if ( chr_is_writable && in.chr_size ) - { - assert( in.chr_size <= (int) sizeof impl->chr_ram ); - memcpy( impl->chr_ram, in.chr, in.chr_size ); - all_tiles_modified(); - } -} - -static BOOST::uint8_t const initial_palette [0x20] = -{ - 0x0f,0x01,0x00,0x01,0x00,0x02,0x02,0x0D,0x08,0x10,0x08,0x24,0x00,0x00,0x04,0x2C, - 0x00,0x01,0x34,0x03,0x00,0x04,0x00,0x14,0x00,0x3A,0x00,0x02,0x00,0x20,0x2C,0x08 -}; - -void Nes_Ppu_Impl::reset( bool full_reset ) -{ - w2000 = 0; - w2001 = 0; - r2002 = 0x80; - r2007 = 0; - open_bus = 0; - decay_low = 0; - decay_high = 0; - second_write = false; - vram_temp = 0; - pixel_x = 0; - - if ( full_reset ) - { - vram_addr = 0; - w2003 = 0; - memset( impl->chr_ram, 0xff, sizeof impl->chr_ram ); - memset( impl->nt_ram, 0xff, sizeof impl->nt_ram ); - memcpy( palette, initial_palette, sizeof palette ); - } - - set_nt_banks( 0, 0, 0, 0 ); - set_chr_bank( 0, chr_addr_size, 0 ); - memset( spr_ram, 0xff, sizeof spr_ram ); - all_tiles_modified(); - if ( max_palette_size > 0 ) - memset( host_palette, 0, max_palette_size * sizeof *host_palette ); -} - -void Nes_Ppu_Impl::capture_palette() -{ - if ( palette_size + palette_increment <= max_palette_size ) - { - palette_offset = (palette_begin + palette_size) * 0x01010101; - - short* out = host_palette + palette_size; - palette_size += palette_increment; - - int i; - - int emph = w2001 << 1 & 0x1C0; - int mono = (w2001 & 1 ? 0x30 : 0x3F); - - for ( i = 0; i < 32; i++ ) - out [i] = (palette [i] & mono) | emph; - - int bg = out [0]; - for ( i = 4; i < 32; i += 4 ) - out [i] = bg; - - memcpy( out + 32, out, 32 * sizeof *out ); - } -} - -void Nes_Ppu_Impl::run_hblank( int count ) -{ - require( count >= 0 ); - - long addr = (vram_addr & 0x7be0) + (vram_temp & 0x41f) + (count * 0x1000); - if ( w2001 & 0x08 ) - { - while ( addr >= 0x8000 ) - { - int y = (addr + 0x20) & 0x3e0; - addr = (addr - 0x8000) & ~0x3e0; - if ( y == 30 * 0x20 ) - y = 0x800; - addr ^= y; - } - vram_addr = addr; - } -} - -#ifdef __MWERKS__ - #pragma ppc_unroll_factor_limit 1 // messes up calc_sprite_max_scanlines loop - static int zero = 0; -#else - const int zero = 0; -#endif - -// Tile cache - -inline unsigned long reorder( unsigned long n ) -{ - n |= n << 7; - return ((n << 14) | n); -} - -inline void Nes_Ppu_Impl::update_tile( int index ) -{ - const byte* in = chr_data + (index) * bytes_per_tile; - byte* out = (byte*) tile_cache [index]; - byte* flipped_out = (byte*) flipped_tiles [index]; - - unsigned long bit_mask = 0x11111111 + zero; - - for ( int n = 4; n--; ) - { - // Reorder two lines of two-bit pixels. No bits are wasted, so - // reordered version is also four bytes. - // - // 12345678 to A0E4B1F5C2G6D3H7 - // ABCDEFGH - unsigned long c = - ((reorder( in [0] ) & bit_mask) << 0) | - ((reorder( in [8] ) & bit_mask) << 1) | - ((reorder( in [1] ) & bit_mask) << 2) | - ((reorder( in [9] ) & bit_mask) << 3); - in += 2; - - SET_BE32( out, c ); - out += 4; - - // make horizontally-flipped version - c = ((c >> 28) & 0x000f) | - ((c >> 20) & 0x00f0) | - ((c >> 12) & 0x0f00) | - ((c >> 4) & 0xf000) | - ((c & 0xf000) << 4) | - ((c & 0x0f00) << 12) | - ((c & 0x00f0) << 20) | - ((c & 0x000f) << 28); - SET_BE32( flipped_out, c ); - flipped_out += 4; - } -} - -void Nes_Ppu_Impl::rebuild_chr( unsigned long begin, unsigned long end ) -{ - unsigned end_index = (end + bytes_per_tile - 1) / bytes_per_tile; - for ( unsigned index = begin / bytes_per_tile; index < end_index; index++ ) - update_tile( index ); -} - -void Nes_Ppu_Impl::update_tiles( int first_tile ) -{ - int chunk = 0; - do - { - if ( !(uint32_t&) modified_tiles [chunk] ) - { - chunk += 4; - } - else - { - do - { - int modified = modified_tiles [chunk]; - if ( modified ) - { - modified_tiles [chunk] = 0; - - int index = first_tile + chunk * 8; - do - { - if ( modified & 1 ) - update_tile( index ); - index++; - } - while ( (modified >>= 1) != 0 ); - } - } - while ( ++chunk & 3 ); - } - } - while ( chunk < chr_tile_count / 8 ); -} - -// Sprite max - -template -struct calc_sprite_max_scanlines -{ - static unsigned long func( byte const* sprites, byte* scanlines, int begin ) - { - typedef BOOST::uint32_t uint32_t; - - unsigned long any_hits = 0; - unsigned long const offset = 0x01010101 + zero; - unsigned limit = 239 + height - begin; - for ( int n = 64; n; --n ) - { - int top = *sprites; - sprites += 4; - byte* p = scanlines + top; - if ( (unsigned) (239 - top) < limit ) - { - unsigned long p0 = (uint32_t&) p [0] + offset; - unsigned long p4 = (uint32_t&) p [4] + offset; - (uint32_t&) p [0] = p0; - any_hits |= p0; - (uint32_t&) p [4] = p4; - any_hits |= p4; - if ( height > 8 ) - { - unsigned long p0 = (uint32_t&) p [ 8] + offset; - unsigned long p4 = (uint32_t&) p [12] + offset; - (uint32_t&) p [ 8] = p0; - any_hits |= p0; - (uint32_t&) p [12] = p4; - any_hits |= p4; - } - } - } - - return any_hits; - } -}; - -long Nes_Ppu_Impl::recalc_sprite_max( int scanline ) -{ - int const max_scanline_count = image_height; - - byte sprite_max_scanlines [256 + 16]; - - // recalculate sprites per scanline - memset( sprite_max_scanlines + scanline, 0x78, last_sprite_max_scanline - scanline ); - unsigned long any_hits; - if ( w2000 & 0x20 ) - any_hits = calc_sprite_max_scanlines<16>::func( spr_ram, sprite_max_scanlines, scanline ); - else - any_hits = calc_sprite_max_scanlines<8 >::func( spr_ram, sprite_max_scanlines, scanline ); - - // cause search to terminate past max_scanline_count if none have 8 or more sprites - (uint32_t&) sprite_max_scanlines [max_scanline_count] = 0; - sprite_max_scanlines [max_scanline_count + 3] = 0x80; - - // avoid scan if no possible hits - if ( !(any_hits & 0x80808080) ) - return 0; - - // find soonest scanline with 8 or more sprites - while ( true ) - { - unsigned long const mask = 0x80808080 + zero; - - // check four at a time - byte* pos = &sprite_max_scanlines [scanline]; - unsigned long n = (uint32_t&) *pos; - while ( 1 ) - { - unsigned long x = n & mask; - pos += 4; - n = (uint32_t&) *pos; - if ( x ) - break; - } - - int height = sprite_height(); - int remain = 8; - int i = 0; - - // find which of the four - pos -= 3 + (pos [-4] >> 7 & 1); - pos += 1 - (*pos >> 7 & 1); - pos += 1 - (*pos >> 7 & 1); - assert( *pos & 0x80 ); - - scanline = pos - sprite_max_scanlines; - if ( scanline >= max_scanline_count ) - break; - - // find time that max sprites flag is set (or that it won't be set) - do - { - int relative = scanline - spr_ram [i]; - i += 4; - if ( (unsigned) relative < (unsigned) height && !--remain ) - { - // now use screwey search for 9th sprite - int offset = 0; - while ( i < 0x100 ) - { - int relative = scanline - spr_ram [i + offset]; - //dprintf( "Checking sprite %d [%d]\n", i / 4, offset ); - i += 4; - offset = (offset + 1) & 3; - if ( (unsigned) relative < (unsigned) height ) - { - //dprintf( "sprite max on scanline %d\n", scanline ); - return scanline * scanline_len + (unsigned) i / 2; - } - } - break; - } - } - while ( i < 0x100 ); - scanline++; - } - - return 0; -} - diff --git a/quicknes/nes_emu/Nes_Ppu_Impl.h b/quicknes/nes_emu/Nes_Ppu_Impl.h deleted file mode 100644 index d09f82849d..0000000000 --- a/quicknes/nes_emu/Nes_Ppu_Impl.h +++ /dev/null @@ -1,230 +0,0 @@ -// NES PPU misc functions and setup - -// Nes_Emu 0.7.0 - -#ifndef NES_PPU_IMPL_H -#define NES_PPU_IMPL_H - -#include "nes_data.h" -class Nes_State_; - -class Nes_Ppu_Impl : public ppu_state_t { -public: - typedef BOOST::uint8_t byte; - typedef BOOST::uint32_t uint32_t; - - Nes_Ppu_Impl(); - ~Nes_Ppu_Impl(); - - void reset( bool full_reset ); - - // Setup - blargg_err_t open_chr( const byte*, long size ); - void rebuild_chr( unsigned long begin, unsigned long end ); - void close_chr(); - void save_state( Nes_State_* out ) const; - void load_state( Nes_State_ const& ); - - enum { image_width = 256 }; - enum { image_height = 240 }; - enum { image_left = 8 }; - enum { buffer_width = image_width + 16 }; - enum { buffer_height = image_height }; - - int write_2007( int ); - int peekaddr(int); - - // Host palette - enum { palette_increment = 64 }; - short* host_palette; - int palette_begin; - int max_palette_size; - int palette_size; // set after frame is rendered - - // Mapping - enum { vaddr_clock_mask = 0x1000 }; - void set_nt_banks( int bank0, int bank1, int bank2, int bank3 ); - void set_chr_bank( int addr, int size, long data ); - void set_chr_bank_ex( int addr, int size, long data ); // mmc24 only - - // Nametable and CHR RAM - enum { nt_ram_size = 0x1000 }; - enum { chr_addr_size = 0x2000 }; - enum { bytes_per_tile = 16 }; - enum { chr_tile_count = chr_addr_size / bytes_per_tile }; - enum { mini_offscreen_height = 16 }; // double-height sprite - struct impl_t - { - byte nt_ram [nt_ram_size]; - byte chr_ram [chr_addr_size]; - union { - BOOST::uint32_t clip_buf [256 * 2]; - byte mini_offscreen [buffer_width * mini_offscreen_height]; - }; - }; - impl_t* impl; - enum { scanline_len = 341 }; - - byte spr_ram [0x100]; -protected: - void begin_frame(); - void run_hblank( int ); - int sprite_height() const { return (w2000 >> 2 & 8) + 8; } - -protected: //friend class Nes_Ppu; private: - - int addr_inc; // pre-calculated $2007 increment (based on w2001 & 0x04) - int read_2007( int addr ); - - enum { last_sprite_max_scanline = 240 }; - long recalc_sprite_max( int scanline ); - int first_opaque_sprite_line() /*const*/; - -protected: //friend class Nes_Ppu_Rendering; private: - - unsigned long palette_offset; - int palette_changed; - void capture_palette(); - - bool any_tiles_modified; - bool chr_is_writable; - void update_tiles( int first_tile ); - - typedef uint32_t cache_t; - typedef cache_t cached_tile_t [4]; - cached_tile_t const& get_bg_tile( int index ) /*const*/; - cached_tile_t const& get_sprite_tile( byte const* sprite ) /*const*/; - byte* get_nametable( int addr ) { return nt_banks [addr >> 10 & 3]; }; - -private: - - static int map_palette( int addr ); - int sprite_tile_index( byte const* sprite ) const; - - // Mapping - enum { chr_page_size = 0x400 }; - long chr_pages [chr_addr_size / chr_page_size]; - long chr_pages_ex [chr_addr_size / chr_page_size]; // mmc24 only - long map_chr_addr_peek( unsigned a ) const - { - return chr_pages[a / chr_page_size] + a; - } - - long map_chr_addr( unsigned a ) /*const*/ - { - if (!mmc24_enabled) - return chr_pages [a / chr_page_size] + a; - - // mmc24 calculations - - int page = a >> 12 & 1; - // can't check against bit 3 of address, because quicknes never actually fetches those - int newval0 = (a & 0xff0) != 0xfd0; - int newval1 = (a & 0xff0) == 0xfe0; - - long ret; - if (mmc24_latched[page]) - ret = chr_pages_ex [a / chr_page_size] + a; - else - ret = chr_pages [a / chr_page_size] + a; - - mmc24_latched[page] &= newval0; - mmc24_latched[page] |= newval1; - - return ret; - } - byte* nt_banks [4]; - - bool mmc24_enabled; // true if mmc24 regs need to be latched and checked - byte mmc24_latched [2]; // current latch value for the first\second 4k of memory - - // CHR data - byte const* chr_data; // points to chr ram when there is no read-only data - byte* chr_ram; // always points to impl->chr_ram; makes write_2007() faster - long chr_size; - byte const* map_chr( int addr ) /*const*/ { return &chr_data [map_chr_addr( addr )]; } - - // CHR cache - cached_tile_t* tile_cache; - cached_tile_t* flipped_tiles; - byte* tile_cache_mem; - union { - byte modified_tiles [chr_tile_count / 8]; - uint32_t align_; - }; - void all_tiles_modified(); - void update_tile( int index ); -}; - -inline void Nes_Ppu_Impl::set_nt_banks( int bank0, int bank1, int bank2, int bank3 ) -{ - byte* nt_ram = impl->nt_ram; - nt_banks [0] = &nt_ram [bank0 * 0x400]; - nt_banks [1] = &nt_ram [bank1 * 0x400]; - nt_banks [2] = &nt_ram [bank2 * 0x400]; - nt_banks [3] = &nt_ram [bank3 * 0x400]; -} - -inline int Nes_Ppu_Impl::map_palette( int addr ) -{ - if ( (addr & 3) == 0 ) - addr &= 0x0f; // 0x10, 0x14, 0x18, 0x1c map to 0x00, 0x04, 0x08, 0x0c - return addr & 0x1f; -} - -inline int Nes_Ppu_Impl::sprite_tile_index( byte const* sprite ) const -{ - int tile = sprite [1] + (w2000 << 5 & 0x100); - if ( w2000 & 0x20 ) - tile = (tile & 1) * 0x100 + (tile & 0xfe); - return tile; -} - -inline int Nes_Ppu_Impl::write_2007( int data ) -{ - int addr = vram_addr; - byte* chr_ram = this->chr_ram; // pre-read - int changed = addr + addr_inc; - unsigned const divisor = bytes_per_tile * 8; - int mod_index = (unsigned) addr / divisor % (0x4000 / divisor); - vram_addr = changed; - changed ^= addr; - addr &= 0x3fff; - - // use index into modified_tiles [] since it's calculated sooner than addr is masked - if ( (unsigned) mod_index < 0x2000 / divisor ) - { - // Avoid overhead of checking for read-only CHR; if that is the case, - // this modification will be ignored. - int mod = modified_tiles [mod_index]; - chr_ram [addr] = data; - any_tiles_modified = true; - modified_tiles [mod_index] = mod | (1 << ((unsigned) addr / bytes_per_tile % 8)); - } - else if ( addr < 0x3f00 ) - { - get_nametable( addr ) [addr & 0x3ff] = data; - } - else - { - data &= 0x3f; - byte& entry = palette [map_palette( addr )]; - int changed = entry ^ data; - entry = data; - if ( changed ) - palette_changed = 0x18; - } - - return changed; -} - -inline void Nes_Ppu_Impl::begin_frame() -{ - palette_changed = 0x18; - palette_size = 0; - palette_offset = palette_begin * 0x01010101; - addr_inc = w2000 & 4 ? 32 : 1; -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Ppu_Rendering.cpp b/quicknes/nes_emu/Nes_Ppu_Rendering.cpp deleted file mode 100644 index 2736cd4951..0000000000 --- a/quicknes/nes_emu/Nes_Ppu_Rendering.cpp +++ /dev/null @@ -1,496 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Ppu_Rendering.h" - -#include -#include - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -#ifdef __MWERKS__ - static unsigned zero = 0; // helps CodeWarrior optimizer when added to constants -#else - const unsigned zero = 0; // compile-time constant on other compilers -#endif - -// Nes_Ppu_Impl - -inline Nes_Ppu_Impl::cached_tile_t const& - Nes_Ppu_Impl::get_sprite_tile( byte const* sprite ) /*const*/ -{ - cached_tile_t* tiles = tile_cache; - if ( sprite [2] & 0x40 ) - tiles = flipped_tiles; - int index = sprite_tile_index( sprite ); - - // use index directly, since cached tile is same size as native tile - BOOST_STATIC_ASSERT( sizeof (cached_tile_t) == bytes_per_tile ); - return *(Nes_Ppu_Impl::cached_tile_t*) - ((byte*) tiles + map_chr_addr( index * bytes_per_tile )); -} - -inline Nes_Ppu_Impl::cached_tile_t const& Nes_Ppu_Impl::get_bg_tile( int index ) /*const*/ -{ - // use index directly, since cached tile is same size as native tile - BOOST_STATIC_ASSERT( sizeof (cached_tile_t) == bytes_per_tile ); - return *(Nes_Ppu_Impl::cached_tile_t*) - ((byte*) tile_cache + map_chr_addr( index * bytes_per_tile )); -} - -// Fill - -void Nes_Ppu_Rendering::fill_background( int count ) -{ - ptrdiff_t const next_line = scanline_row_bytes - image_width; - uint32_t* pixels = (uint32_t*) scanline_pixels; - - unsigned long fill = palette_offset; - if ( (vram_addr & 0x3f00) == 0x3f00 ) - { - // PPU uses current palette entry if addr is within palette ram - int color = vram_addr & 0x1f; - if ( !(color & 3) ) - color &= 0x0f; - fill += color * 0x01010101; - } - - for ( int n = count; n--; ) - { - for ( int n = image_width / 16; n--; ) - { - pixels [0] = fill; - pixels [1] = fill; - pixels [2] = fill; - pixels [3] = fill; - pixels += 4; - } - pixels = (uint32_t*) ((byte*) pixels + next_line); - } -} - -void Nes_Ppu_Rendering::clip_left( int count ) -{ - ptrdiff_t next_line = scanline_row_bytes; - byte* p = scanline_pixels; - unsigned long fill = palette_offset; - - for ( int n = count; n--; ) - { - ((uint32_t*) p) [0] = fill; - ((uint32_t*) p) [1] = fill; - p += next_line; - } -} - -void Nes_Ppu_Rendering::save_left( int count ) -{ - ptrdiff_t next_line = scanline_row_bytes; - byte* in = scanline_pixels; - uint32_t* out = impl->clip_buf; - - for ( int n = count; n--; ) - { - unsigned long in0 = ((uint32_t*) in) [0]; - unsigned long in1 = ((uint32_t*) in) [1]; - in += next_line; - out [0] = in0; - out [1] = in1; - out += 2; - } -} - -void Nes_Ppu_Rendering::restore_left( int count ) -{ - ptrdiff_t next_line = scanline_row_bytes; - byte* out = scanline_pixels; - uint32_t* in = impl->clip_buf; - - for ( int n = count; n--; ) - { - unsigned long in0 = in [0]; - unsigned long in1 = in [1]; - in += 2; - ((uint32_t*) out) [0] = in0; - ((uint32_t*) out) [1] = in1; - out += next_line; - } -} - -// Background - -void Nes_Ppu_Rendering::draw_background_( int remain ) -{ - // Draws 'remain' background scanlines. Does not modify vram_addr. - - int vram_addr = this->vram_addr & 0x7fff; - byte* row_pixels = scanline_pixels - pixel_x; - int left_clip = (w2001 >> 1 & 1) ^ 1; - row_pixels += left_clip * 8; - do - { - // scanlines until next row - int height = 8 - (vram_addr >> 12); - if ( height > remain ) - height = remain; - - // handle hscroll change before next scanline - int hscroll_changed = (vram_addr ^ vram_temp) & 0x41f; - int addr = vram_addr; - if ( hscroll_changed ) - { - vram_addr ^= hscroll_changed; - height = 1; // hscroll will change after first line - } - remain -= height; - - // increment address for next row - vram_addr += height << 12; - assert( vram_addr < 0x10000 ); - if ( vram_addr & 0x8000 ) - { - int y = (vram_addr + 0x20) & 0x3e0; - vram_addr &= 0x7fff & ~0x3e0; - if ( y == 30 * 0x20 ) - y = 0x800; // toggle vertical nametable - vram_addr ^= y; - } - - // nametable change usually occurs in middle of row - byte const* nametable = get_nametable( addr ); - byte const* nametable2 = get_nametable( addr ^ 0x400 ); - int count2 = addr & 31; - int count = 32 - count2 - left_clip; - - // this conditional is commented out because of mmc2\4 - // normally, the extra row of pixels is only fetched when pixel_ x is not 0, which makes sense - // but here, we need a correct fetch pattern to pick up 0xfd\0xfe tiles off the edge of the display - - // this doesn't cause any problems with buffer overflow because the framebuffer we're rendering to is - // already guarded (width = 272) - // this doesn't give us a fully correct ppu fetch pattern, but it's close enough for punch out - - //if ( pixel_x ) - count2++; - - byte const* attr_table = &nametable [0x3c0 | (addr >> 4 & 0x38)]; - int bg_bank = (w2000 << 4) & 0x100; - addr += left_clip; - - // output pixels - ptrdiff_t const row_bytes = scanline_row_bytes; - byte* pixels = row_pixels; - row_pixels += height * row_bytes; - - unsigned long const mask = 0x03030303 + zero; - unsigned long const attrib_factor = 0x04040404 + zero; - - if ( height == 8 ) - { - // unclipped - assert( (addr >> 12) == 0 ); - addr &= 0x03ff; - int const fine_y = 0; - int const clipped = false; - #include "Nes_Ppu_Bg.h" - } - else - { - // clipped - int const fine_y = addr >> 12; - addr &= 0x03ff; - height -= fine_y & 1; - int const clipped = true; - #include "Nes_Ppu_Bg.h" - } - } - while ( remain ); -} - -// Sprites - -void Nes_Ppu_Rendering::draw_sprites_( int begin, int end ) -{ - // Draws sprites on scanlines begin through end - 1. Handles clipping. - - int const sprite_height = this->sprite_height(); - int end_minus_one = end - 1; - int begin_minus_one = begin - 1; - int index = 0; - do - { - byte const* sprite = &spr_ram [index]; - index += 4; - - // find if sprite is visible - int top_minus_one = sprite [0]; - int visible = end_minus_one - top_minus_one; - if ( visible <= 0 ) - continue; // off bottom - - // quickly determine whether sprite is unclipped - int neg_vis = visible - sprite_height; - int neg_skip = top_minus_one - begin_minus_one; - if ( (neg_skip | neg_vis) >= 0 ) // neg_skip >= 0 && neg_vis >= 0 - { - // unclipped - #ifndef NDEBUG - int top = sprite [0] + 1; - assert( (top + sprite_height) > begin && top < end ); - assert( begin <= top && top + sprite_height <= end ); - #endif - - int const skip = 0; - int visible = sprite_height; - - #define CLIPPED 0 - #include "Nes_Ppu_Sprites.h" - } - else - { - // clipped - if ( neg_vis > 0 ) - visible -= neg_vis; - - if ( neg_skip > 0 ) - neg_skip = 0; - visible += neg_skip; - - if ( visible <= 0 ) - continue; // off top - - // visible and clipped - #ifndef NDEBUG - int top = sprite [0] + 1; - assert( (top + sprite_height) > begin && top < end ); - assert( top < begin || top + sprite_height > end ); - #endif - - int skip = -neg_skip; - - //dprintf( "begin: %d, end: %d, top: %d, skip: %d, visible: %d\n", - // begin, end, top_minus_one + 1, skip, visible ); - - #define CLIPPED 1 - #include "Nes_Ppu_Sprites.h" - } - } - while ( index < 0x100 ); -} - -void Nes_Ppu_Rendering::check_sprite_hit( int begin, int end ) -{ - // Checks for sprite 0 hit on scanlines begin through end - 1. - // Updates sprite_hit_found. Background (but not sprites) must have - // already been rendered for the scanlines. - - // clip - int top = spr_ram [0] + 1; - int skip = begin - top; - if ( skip < 0 ) - skip = 0; - - top += skip; - int visible = end - top; - if ( visible <= 0 ) - return; // not visible - - int height = sprite_height(); - if ( visible >= height ) - { - visible = height; - sprite_hit_found = -1; // signal that no more hit checking will take place - } - - // pixels - ptrdiff_t next_row = this->scanline_row_bytes; - byte const* bg = this->scanline_pixels + spr_ram [3] + (top - begin) * next_row; - cache_t const* lines = get_sprite_tile( spr_ram ); - - // left edge clipping - int start_x = 0; - if ( spr_ram [3] < 8 && (w2001 & 0x01e) != 0x1e ) - { - if ( spr_ram [3] == 0 ) - return; // won't hit - start_x = 8 - spr_ram [3]; - } - - // vertical flip - int final = skip + visible; - if ( spr_ram [2] & 0x80 ) - { - skip += height - 1; - final = skip - visible; - } - - // check each line - unsigned long const mask = 0x01010101 + zero; - do - { - // get pixels for line - unsigned long line = lines [skip >> 1]; - unsigned long hit0 = ((uint32_t*) bg) [0]; - unsigned long hit1 = ((uint32_t*) bg) [1]; - bg += next_row; - line >>= skip << 1 & 2; - line |= line >> 1; - - // check for hits - hit0 = ((hit0 >> 1) | hit0) & (line >> 4); - hit1 = ((hit1 >> 1) | hit1) & line; - if ( (hit0 | hit1) & mask ) - { - // write to memory to avoid endian issues - uint32_t quads [3]; - quads [0] = hit0; - quads [1] = hit1; - - // find which pixel hit - int x = start_x; - do - { - if ( ((byte*) quads) [x] & 1 ) - { - x += spr_ram [3]; - if ( x >= 255 ) - break; // ignore right edge - - if ( spr_ram [2] & 0x80 ) - skip = height - 1 - skip; // vertical flip - int y = spr_ram [0] + 1 + skip; - sprite_hit_found = y * scanline_len + x; - - return; - } - } - while ( x++ < 7 ); - } - if ( skip > final ) - skip -= 2; - skip++; - } - while ( skip != final ); -} - -// Draw scanlines - -inline bool Nes_Ppu_Rendering::sprite_hit_possible( int scanline ) const -{ - return !sprite_hit_found && spr_ram [0] <= scanline && (w2001 & 0x18) == 0x18; -} - -void Nes_Ppu_Rendering::draw_scanlines( int start, int count, - byte* pixels, long pitch, int mode ) -{ - assert( start + count <= image_height ); - assert( pixels ); - - scanline_pixels = pixels + image_left; - scanline_row_bytes = pitch; - - int const obj_mask = 2; - int const bg_mask = 1; - int draw_mode = (w2001 >> 3) & 3; - int clip_mode = (~w2001 >> 1) & draw_mode; - - if ( !(draw_mode & bg_mask) ) - { - // no background - clip_mode |= bg_mask; // avoid unnecessary save/restore - if ( mode & bg_mask ) - fill_background( count ); - } - - if ( start == 0 && mode & 1 ) - memset( sprite_scanlines, max_sprites - sprite_limit, 240 ); - - if ( (draw_mode &= mode) ) - { - // sprites and/or background are being rendered - - if ( any_tiles_modified && chr_is_writable ) - { - any_tiles_modified = false; - update_tiles( 0 ); - } - - if ( draw_mode & bg_mask ) - { - //dprintf( "bg %3d-%3d\n", start, start + count - 1 ); - draw_background_( count ); - - if ( clip_mode == bg_mask ) - clip_left( count ); - - if ( sprite_hit_possible( start + count ) ) - check_sprite_hit( start, start + count ); - } - - if ( draw_mode & obj_mask ) - { - // when clipping just sprites, save left strip then restore after drawing them - if ( clip_mode == obj_mask ) - save_left( count ); - - //dprintf( "obj %3d-%3d\n", start, start + count - 1 ); - - draw_sprites_( start, start + count ); - - if ( clip_mode == obj_mask ) - restore_left( count ); - - if ( clip_mode == (obj_mask | bg_mask) ) - clip_left( count ); - } - } - - scanline_pixels = NULL; -} - -void Nes_Ppu_Rendering::draw_background( int start, int count ) -{ - // always capture palette at least once per frame - if ( (start + count >= 240 && !palette_size) || (w2001 & palette_changed) ) - { - palette_changed = false; - capture_palette(); - } - - if ( host_pixels ) - { - draw_scanlines( start, count, host_pixels + host_row_bytes * start, host_row_bytes, 1 ); - } - else if ( sprite_hit_possible( start + count ) ) - { - // not rendering, but still handle sprite hit using mini graphics buffer - int y = spr_ram [0] + 1; - int skip = min( count, max( y - start, 0 ) ); - int visible = min( count - skip, sprite_height() ); - - assert( skip + visible <= count ); - assert( visible <= mini_offscreen_height ); - - if ( visible > 0 ) - { - run_hblank( skip ); - draw_scanlines( start + skip, visible, impl->mini_offscreen, buffer_width, 3 ); - } - } -} - diff --git a/quicknes/nes_emu/Nes_Ppu_Rendering.h b/quicknes/nes_emu/Nes_Ppu_Rendering.h deleted file mode 100644 index 68929973f9..0000000000 --- a/quicknes/nes_emu/Nes_Ppu_Rendering.h +++ /dev/null @@ -1,63 +0,0 @@ - -// NES PPU emulator graphics rendering - -// Nes_Emu 0.7.0 - -#ifndef NES_PPU_RENDERING_H -#define NES_PPU_RENDERING_H - -#include "Nes_Ppu_Impl.h" - -class Nes_Ppu_Rendering : public Nes_Ppu_Impl { - typedef Nes_Ppu_Impl base; -public: - Nes_Ppu_Rendering(); - - int sprite_limit; - - byte* host_pixels; - long host_row_bytes; - -protected: - - long sprite_hit_found; // -1: sprite 0 didn't hit, 0: no hit so far, > 0: y * 341 + x - void draw_background( int start, int count ); - void draw_sprites( int start, int count ); - -private: - - void draw_scanlines( int start, int count, byte* pixels, long pitch, int mode ); - void draw_background_( int count ); - - // destination for draw functions; avoids extra parameters - byte* scanline_pixels; - long scanline_row_bytes; - - // fill/copy - void fill_background( int count ); - void clip_left( int count ); - void save_left( int count ); - void restore_left( int count ); - - // sprites - enum { max_sprites = 64 }; - byte sprite_scanlines [image_height]; // number of sprites on each scanline - void draw_sprites_( int start, int count ); - bool sprite_hit_possible( int scanline ) const; - void check_sprite_hit( int begin, int end ); -}; - -inline Nes_Ppu_Rendering::Nes_Ppu_Rendering() -{ - sprite_limit = 8; - host_pixels = NULL; -} - -inline void Nes_Ppu_Rendering::draw_sprites( int start, int count ) -{ - assert( host_pixels ); - draw_scanlines( start, count, host_pixels + host_row_bytes * start, host_row_bytes, 2 ); -} - -#endif - diff --git a/quicknes/nes_emu/Nes_Ppu_Sprites.h b/quicknes/nes_emu/Nes_Ppu_Sprites.h deleted file mode 100644 index 4358ad74c0..0000000000 --- a/quicknes/nes_emu/Nes_Ppu_Sprites.h +++ /dev/null @@ -1,132 +0,0 @@ - -int sprite_2 = sprite [2]; - -// pixels -ptrdiff_t next_row = this->scanline_row_bytes; -byte* out = this->scanline_pixels + sprite [3] + - (top_minus_one + skip - begin_minus_one) * next_row; -cache_t const* lines = get_sprite_tile( sprite ); - -int dir = 1; -byte* scanlines = this->sprite_scanlines + 1 + top_minus_one + skip; - -if ( sprite_2 & 0x80 ) -{ - // vertical flip - out -= next_row; - out += visible * next_row; - next_row = -next_row; - dir = -1; - scanlines += visible - 1; - #if CLIPPED - int height = this->sprite_height(); - skip = height - skip - visible; - assert( skip + visible <= height ); - #endif -} - -// attributes -unsigned long offset = (sprite_2 & 3) * 0x04040404 + (this->palette_offset + 0x10101010); - -unsigned long const mask = 0x03030303 + zero; -unsigned long const maskgen = 0x80808080 + zero; - -#define DRAW_PAIR( shift ) { \ - int sprite_count = *scanlines; \ - CALC_FOUR( ((uint32_t*) out) [0], (line >> (shift + 4)), out0 ) \ - CALC_FOUR( ((uint32_t*) out) [1], (line >> shift), out1 ) \ - if ( sprite_count < this->max_sprites ) { \ - ((uint32_t*) out) [0] = out0; \ - ((uint32_t*) out) [1] = out1; \ - } \ - if ( CLIPPED ) visible--; \ - out += next_row; \ - *scanlines = sprite_count + 1; \ - scanlines += dir; \ - if ( CLIPPED && !visible ) break; \ -} - -if ( !(sprite_2 & 0x20) ) -{ - // front - unsigned long const maskgen2 = 0x7f7f7f7f + zero; - - #define CALC_FOUR( in, line, out ) \ - unsigned long out; \ - { \ - unsigned long bg = in; \ - unsigned long sp = line & mask; \ - unsigned long bgm = maskgen2 + ((bg >> 4) & mask); \ - unsigned long spm = (maskgen - sp) & maskgen2; \ - unsigned long m = (bgm & spm) >> 2; \ - out = (bg & ~m) | ((sp + offset) & m); \ - } - - #if CLIPPED - lines += skip >> 1; - unsigned long line = *lines++; - if ( skip & 1 ) - goto front_skip; - - while ( true ) - { - DRAW_PAIR( 0 ) - front_skip: - DRAW_PAIR( 2 ) - line = *lines++; - } - #else - for ( int n = visible >> 1; n--; ) - { - unsigned long line = *lines++; - DRAW_PAIR( 0 ) - DRAW_PAIR( 2 ) - } - #endif - - #undef CALC_FOUR -} -else -{ - // behind - unsigned long const omask = 0x20202020 + zero; - unsigned long const bg_or = 0xc3c3c3c3 + zero; - - #define CALC_FOUR( in, line, out ) \ - unsigned long out; \ - { \ - unsigned long bg = in; \ - unsigned long sp = line & mask; \ - unsigned long bgm = maskgen - (bg & mask); \ - unsigned long spm = maskgen - sp; \ - out = (bg & (bgm | bg_or)) | (spm & omask) | \ - (((offset & spm) + sp) & ~(bgm >> 2)); \ - } - - #if CLIPPED - lines += skip >> 1; - unsigned long line = *lines++; - if ( skip & 1 ) - goto back_skip; - - while ( true ) - { - DRAW_PAIR( 0 ) - back_skip: - DRAW_PAIR( 2 ) - line = *lines++; - } - #else - for ( int n = visible >> 1; n--; ) - { - unsigned long line = *lines++; - DRAW_PAIR( 0 ) - DRAW_PAIR( 2 ) - } - #endif - - #undef CALC_FOUR -} - -#undef CLIPPED -#undef DRAW_PAIR diff --git a/quicknes/nes_emu/Nes_State.cpp b/quicknes/nes_emu/Nes_State.cpp deleted file mode 100644 index 359644a08d..0000000000 --- a/quicknes/nes_emu/Nes_State.cpp +++ /dev/null @@ -1,293 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_State.h" - -#include -#include - -#include "blargg_endian.h" -#include "Nes_Emu.h" -#include "Nes_Mapper.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -int mem_differs( void const* p, int cmp, unsigned long s ) -{ - unsigned char const* cp = (unsigned char*) p; - while ( s-- ) - { - if ( *cp++ != cmp ) - return 1; - } - return 0; -} - -Nes_State::Nes_State() -{ - Nes_State_::cpu = &this->cpu; - Nes_State_::joypad = &this->joypad; - Nes_State_::apu = &this->apu; - Nes_State_::ppu = &this->ppu; - Nes_State_::mapper = &this->mapper; - Nes_State_::ram = this->ram; - Nes_State_::sram = this->sram; - Nes_State_::spr_ram = this->spr_ram; - Nes_State_::nametable = this->nametable; - Nes_State_::chr = this->chr; -} - -void Nes_State_::clear() -{ - memset( &nes, 0, sizeof nes ); - nes.frame_count = static_cast(invalid_frame_count); - - nes_valid = false; - cpu_valid = false; - joypad_valid = false; - apu_valid = false; - ppu_valid = false; - mapper_valid = false; - ram_valid = false; - sram_size = 0; - spr_ram_valid = false; - nametable_size = 0; - chr_size = 0; -} - -// write - -blargg_err_t Nes_State_Writer::end( Nes_Emu const& emu ) -{ - Nes_State* state = BLARGG_NEW Nes_State; - CHECK_ALLOC( state ); - emu.save_state( state ); - blargg_err_t err = end( *state ); - delete state; - return err; -} - -blargg_err_t Nes_State_Writer::end( Nes_State const& ss ) -{ - RETURN_ERR( ss.write_blocks( *this ) ); - return Nes_File_Writer::end(); -} - -blargg_err_t Nes_State::write( Auto_File_Writer out ) const -{ - Nes_State_Writer writer; - RETURN_ERR( writer.begin( out ) ); - return writer.end( *this ); -} - -blargg_err_t Nes_State_::write_blocks( Nes_File_Writer& out ) const -{ - if ( nes_valid ) - { - nes_state_t s = nes; - s.timestamp *= 5; - RETURN_ERR( write_nes_state( out, s ) ); - } - - if ( cpu_valid ) - { - cpu_state_t s; - memset( &s, 0, sizeof s ); - s.pc = cpu->pc; - s.s = cpu->sp; - s.a = cpu->a; - s.x = cpu->x; - s.y = cpu->y; - s.p = cpu->status; - RETURN_ERR( write_nes_state( out, s ) ); - } - - if ( ppu_valid ) - { - ppu_state_t s = *ppu; - RETURN_ERR( write_nes_state( out, s ) ); - } - - if ( apu_valid ) - { - apu_state_t s = *apu; - RETURN_ERR( write_nes_state( out, s ) ); - } - - if ( joypad_valid ) - { - joypad_state_t s = *joypad; - RETURN_ERR( write_nes_state( out, s ) ); - } - - if ( mapper_valid ) - RETURN_ERR( out.write_block( FOUR_CHAR('MAPR'), mapper->data, mapper->size ) ); - - if ( ram_valid ) - RETURN_ERR( out.write_block( FOUR_CHAR('LRAM'), ram, ram_size ) ); - - if ( spr_ram_valid ) - RETURN_ERR( out.write_block( FOUR_CHAR('SPRT'), spr_ram, spr_ram_size ) ); - - if ( nametable_size ) - { - check( nametable_size == 0x800 || nametable_size == 0x1000 ); - RETURN_ERR( out.write_block_header( FOUR_CHAR('NTAB'), nametable_size ) ); - RETURN_ERR( out.write( nametable, 0x800 ) ); - if ( nametable_size > 0x800 ) - RETURN_ERR( out.write( chr, 0x800 ) ); - } - - if ( chr_size ) - RETURN_ERR( out.write_block( FOUR_CHAR('CHRR'), chr, chr_size ) ); - -#ifdef __LIBRETRO__ // Maintain constant save state size. - if ( sram_size ) - RETURN_ERR( out.write_block( FOUR_CHAR('SRAM'), sram, sram_size ) ); -#else - // only save sram if it's been modified - if ( sram_size && mem_differs( sram, 0xff, sram_size ) ) - RETURN_ERR( out.write_block( FOUR_CHAR('SRAM'), sram, sram_size ) ); -#endif - - return 0; -} - -// read - -Nes_State_Reader::Nes_State_Reader() { state_ = 0; owned = 0; } - -Nes_State_Reader::~Nes_State_Reader() { delete owned; } - -blargg_err_t Nes_State_Reader::begin( Auto_File_Reader dr, Nes_State* out ) -{ - state_ = out; - if ( !out ) - CHECK_ALLOC( state_ = owned = BLARGG_NEW Nes_State ); - - RETURN_ERR( Nes_File_Reader::begin( dr ) ); - if ( block_tag() != state_file_tag ) - return "Not a state snapshot file"; - return 0; -} - -blargg_err_t Nes_State::read( Auto_File_Reader in ) -{ - Nes_State_Reader reader; - RETURN_ERR( reader.begin( in, this ) ); - while ( !reader.done() ) - RETURN_ERR( reader.next_block() ); - - return 0; -} - -blargg_err_t Nes_State_Reader::next_block() -{ - if ( depth() != 0 ) - return Nes_File_Reader::next_block(); - return state_->read_blocks( *this ); -} - -void Nes_State_::set_nes_state( nes_state_t const& s ) -{ - nes = s; - nes.timestamp /= 5; - nes_valid = true; -} - -blargg_err_t Nes_State_::read_blocks( Nes_File_Reader& in ) -{ - while ( true ) - { - RETURN_ERR( in.next_block() ); - switch ( in.block_tag() ) - { - case nes_state_t::tag: - memset( &nes, 0, sizeof nes ); - RETURN_ERR( read_nes_state( in, &nes ) ); - set_nes_state( nes ); - break; - - case cpu_state_t::tag: { - cpu_state_t s; - memset( &s, 0, sizeof s ); - RETURN_ERR( read_nes_state( in, &s ) ); - cpu->pc = s.pc; - cpu->sp = s.s; - cpu->a = s.a; - cpu->x = s.x; - cpu->y = s.y; - cpu->status = s.p; - cpu_valid = true; - break; - } - - case ppu_state_t::tag: - memset( ppu, 0, sizeof *ppu ); - RETURN_ERR( read_nes_state( in, ppu ) ); - ppu_valid = true; - break; - - case apu_state_t::tag: - memset( apu, 0, sizeof *apu ); - RETURN_ERR( read_nes_state( in, apu ) ); - apu_valid = true; - break; - - case joypad_state_t::tag: - memset( joypad, 0, sizeof *joypad ); - RETURN_ERR( read_nes_state( in, joypad ) ); - joypad_valid = true; - break; - - case FOUR_CHAR('MAPR'): - mapper->size = in.remain(); - RETURN_ERR( in.read_block_data( mapper->data, sizeof mapper->data ) ); - mapper_valid = true; - break; - - case FOUR_CHAR('SPRT'): - spr_ram_valid = true; - RETURN_ERR( in.read_block_data( spr_ram, spr_ram_size ) ); - break; - - case FOUR_CHAR('NTAB'): - nametable_size = in.remain(); - check( nametable_size == 0x800 || nametable_size == 0x1000 ); - RETURN_ERR( in.read( nametable, 0x800 ) ); - if ( nametable_size > 0x800 ) - RETURN_ERR( in.read( chr, 0x800 ) ); - break; - - case FOUR_CHAR('LRAM'): - ram_valid = true; - RETURN_ERR( in.read_block_data( ram, ram_size ) ); - break; - - case FOUR_CHAR('CHRR'): - chr_size = in.remain(); - RETURN_ERR( in.read_block_data( chr, chr_max ) ); - break; - - case FOUR_CHAR('SRAM'): - sram_size = in.remain(); - RETURN_ERR( in.read_block_data( sram, sram_max ) ); - break; - - default: - return 0; - } - } -} - diff --git a/quicknes/nes_emu/Nes_State.h b/quicknes/nes_emu/Nes_State.h deleted file mode 100644 index 97ec8e2107..0000000000 --- a/quicknes/nes_emu/Nes_State.h +++ /dev/null @@ -1,142 +0,0 @@ - -// NES state snapshot for saving and restoring emulator state - -// Nes_Emu 0.7.0 - -#ifndef NES_STATE_H -#define NES_STATE_H - -#include "Nes_File.h" -#include "Nes_Cpu.h" -class Nes_Emu; -class Nes_State; - -typedef long frame_count_t; - -// Writes state to a file -class Nes_State_Writer : public Nes_File_Writer { -public: - // Begin writing file - blargg_err_t begin( Auto_File_Writer ); - - // Write emulator's current state to file and end - blargg_err_t end( Nes_Emu const& ); - - // Write state to file and end - blargg_err_t end( Nes_State const& ); -}; - -// Reads state from a file -class Nes_State_Reader : public Nes_File_Reader { -public: - - // Begin reading state snapshot from file - blargg_err_t begin( Auto_File_Reader, Nes_State* = 0 ); - - // Go to next unrecognized block in file - blargg_err_t next_block(); - - // State as read from file. Only valid after all blocks have been read. - Nes_State const& state() const; - -public: - Nes_State_Reader(); - ~Nes_State_Reader(); -private: - Nes_State* owned; - Nes_State* state_; -}; - -class Nes_State_ { -public: - - blargg_err_t write_blocks( Nes_File_Writer& ) const; - void set_nes_state( nes_state_t const& ); - blargg_err_t read_blocks( Nes_File_Reader& ); - - enum { ram_size = 0x800 }; - enum { sram_max = 0x2000 }; - enum { spr_ram_size = 0x100 }; - enum { nametable_max = 0x800 }; - enum { chr_max = 0x2000 }; - BOOST::uint8_t *ram, *sram, *spr_ram, *nametable, *chr; - nes_state_t nes; - Nes_Cpu::registers_t* cpu; - joypad_state_t* joypad; - apu_state_t* apu; - ppu_state_t* ppu; - mapper_state_t* mapper; - - bool nes_valid, cpu_valid, joypad_valid, apu_valid, ppu_valid; - bool mapper_valid, ram_valid, spr_ram_valid; - short sram_size, nametable_size, chr_size; - - // Invalidate all state - void clear(); - - // Change timestamp - void set_timestamp( frame_count_t ); - - // Timestamp snapshot was taken at - frame_count_t timestamp() const; -}; - -// Snapshot of emulator state -class Nes_State : private Nes_State_ { -public: - - Nes_State(); - -#if 0 // What is this? - Nes_State_::set_timestamp; - Nes_State_::timestamp; - Nes_State_::clear; -#endif - - // Write snapshot to file - blargg_err_t write( Auto_File_Writer ) const; - - // Read snapshot from file - blargg_err_t read( Auto_File_Reader ); - -private: - Nes_Cpu::registers_t cpu; - joypad_state_t joypad; - apu_state_t apu; - ppu_state_t ppu; - mapper_state_t mapper; - BOOST::uint8_t ram [ram_size]; - BOOST::uint8_t sram [sram_max]; - BOOST::uint8_t spr_ram [spr_ram_size]; - BOOST::uint8_t nametable [nametable_max]; - BOOST::uint8_t chr [chr_max]; - - friend class Nes_Emu; - friend class Nes_State_Writer; - friend class Nes_State_Reader; - friend class Nes_Recorder; -public: - blargg_err_t read_sta_file( Auto_File_Reader ); -}; - -frame_count_t const invalid_frame_count = LONG_MAX / 2 + 1; // a large positive value - -int mem_differs( void const* in, int compare, unsigned long count ); - -inline Nes_State const& Nes_State_Reader::state() const -{ - assert( depth() == 0 && block_type() == group_end ); - return *state_; -} - -inline blargg_err_t Nes_State_Writer::begin( Auto_File_Writer dw ) -{ - return Nes_File_Writer::begin( dw, state_file_tag ); -} - -inline void Nes_State_::set_timestamp( frame_count_t t ) { nes.frame_count = t; } - -inline frame_count_t Nes_State_::timestamp() const { return nes.frame_count; } - -#endif - diff --git a/quicknes/nes_emu/Nes_Vrc6_Apu.cpp b/quicknes/nes_emu/Nes_Vrc6_Apu.cpp deleted file mode 100644 index b100c7e9d3..0000000000 --- a/quicknes/nes_emu/Nes_Vrc6_Apu.cpp +++ /dev/null @@ -1,217 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Nes_Vrc6_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -Nes_Vrc6_Apu::Nes_Vrc6_Apu() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -Nes_Vrc6_Apu::~Nes_Vrc6_Apu() -{ -} - -void Nes_Vrc6_Apu::reset() -{ - last_time = 0; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc& osc = oscs [i]; - for ( int j = 0; j < reg_count; j++ ) - osc.regs [j] = 0; - osc.delay = 0; - osc.last_amp = 0; - osc.phase = 1; - osc.amp = 0; - } -} - -void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -void Nes_Vrc6_Apu::run_until( nes_time_t time ) -{ - require( time >= last_time ); - run_square( oscs [0], time ); - run_square( oscs [1], time ); - run_saw( time ); - last_time = time; -} - -void Nes_Vrc6_Apu::write_osc( nes_time_t time, int osc_index, int reg, int data ) -{ - require( (unsigned) osc_index < osc_count ); - require( (unsigned) reg < reg_count ); - - run_until( time ); - oscs [osc_index].regs [reg] = data; -} - -void Nes_Vrc6_Apu::end_frame( nes_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const -{ - out->saw_amp = oscs [2].amp; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc const& osc = oscs [i]; - for ( int r = 0; r < reg_count; r++ ) - out->regs [i] [r] = osc.regs [r]; - - out->delays [i] = osc.delay; - out->phases [i] = osc.phase; - } -} - -void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in ) -{ - reset(); - oscs [2].amp = in.saw_amp; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc& osc = oscs [i]; - for ( int r = 0; r < reg_count; r++ ) - osc.regs [r] = in.regs [i] [r]; - - osc.delay = in.delays [i]; - osc.phase = in.phases [i]; - } - if ( !oscs [2].phase ) - oscs [2].phase = 1; -} - -void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, nes_time_t end_time ) -{ - Blip_Buffer* output = osc.output; - if ( !output ) - return; - - int volume = osc.regs [0] & 15; - if ( !(osc.regs [2] & 0x80) ) - volume = 0; - - int gate = osc.regs [0] & 0x80; - int duty = ((osc.regs [0] >> 4) & 7) + 1; - int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; - nes_time_t time = last_time; - if ( delta ) - { - osc.last_amp += delta; - square_synth.offset( time, delta, output ); - } - - time += osc.delay; - osc.delay = 0; - int period = osc.period(); - if ( volume && !gate && period > 4 ) - { - if ( time < end_time ) - { - int phase = osc.phase; - - do - { - phase++; - if ( phase == 16 ) - { - phase = 0; - osc.last_amp = volume; - square_synth.offset( time, volume, output ); - } - if ( phase == duty ) - { - osc.last_amp = 0; - square_synth.offset( time, -volume, output ); - } - time += period; - } - while ( time < end_time ); - - osc.phase = phase; - } - osc.delay = time - end_time; - } -} - -void Nes_Vrc6_Apu::run_saw( nes_time_t end_time ) -{ - Vrc6_Osc& osc = oscs [2]; - Blip_Buffer* output = osc.output; - if ( !output ) - return; - - int amp = osc.amp; - int amp_step = osc.regs [0] & 0x3F; - nes_time_t time = last_time; - int last_amp = osc.last_amp; - if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) ) - { - osc.delay = 0; - int delta = (amp >> 3) - last_amp; - last_amp = amp >> 3; - saw_synth.offset( time, delta, output ); - } - else - { - time += osc.delay; - if ( time < end_time ) - { - int period = osc.period() * 2; - int phase = osc.phase; - - do - { - if ( --phase == 0 ) - { - phase = 7; - amp = 0; - } - - int delta = (amp >> 3) - last_amp; - if ( delta ) - { - last_amp = amp >> 3; - saw_synth.offset( time, delta, output ); - } - - time += period; - amp = (amp + amp_step) & 0xFF; - } - while ( time < end_time ); - - osc.phase = phase; - osc.amp = amp; - } - - osc.delay = time - end_time; - } - - osc.last_amp = last_amp; -} - diff --git a/quicknes/nes_emu/Nes_Vrc6_Apu.h b/quicknes/nes_emu/Nes_Vrc6_Apu.h deleted file mode 100644 index aab2fe5d31..0000000000 --- a/quicknes/nes_emu/Nes_Vrc6_Apu.h +++ /dev/null @@ -1,99 +0,0 @@ - -// Konami VRC6 sound chip emulator - -// Nes_Snd_Emu 0.1.7 - -#ifndef NES_VRC6_APU_H -#define NES_VRC6_APU_H - -#include "Nes_Apu.h" -#include "Blip_Buffer.h" - -struct vrc6_apu_state_t; - -class Nes_Vrc6_Apu { -public: - Nes_Vrc6_Apu(); - ~Nes_Vrc6_Apu(); - - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); - enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); - void end_frame( nes_time_t ); - void save_state( vrc6_apu_state_t* ) const; - void load_state( vrc6_apu_state_t const& ); - - // Oscillator 0 write-only registers are at $9000-$9002 - // Oscillator 1 write-only registers are at $A000-$A002 - // Oscillator 2 write-only registers are at $B000-$B002 - enum { reg_count = 3 }; - enum { base_addr = 0x9000 }; - enum { addr_step = 0x1000 }; - void write_osc( nes_time_t, int osc, int reg, int data ); - -private: - // noncopyable - Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); - Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); - - struct Vrc6_Osc - { - BOOST::uint8_t regs [3]; - Blip_Buffer* output; - int delay; - int last_amp; - int phase; - int amp; // only used by saw - - int period() const - { - return (regs [2] & 0x0f) * 0x100L + regs [1] + 1; - } - }; - - Vrc6_Osc oscs [osc_count]; - nes_time_t last_time; - - Blip_Synth saw_synth; - Blip_Synth square_synth; - - void run_until( nes_time_t ); - void run_square( Vrc6_Osc& osc, nes_time_t ); - void run_saw( nes_time_t ); -}; - -struct vrc6_apu_state_t -{ - BOOST::uint8_t regs [3] [3]; - BOOST::uint8_t saw_amp; - BOOST::uint16_t delays [3]; - BOOST::uint8_t phases [3]; - BOOST::uint8_t unused; -}; -BOOST_STATIC_ASSERT( sizeof (vrc6_apu_state_t) == 20 ); - -inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Vrc6_Apu::volume( double v ) -{ - double const factor = 0.0967 * 2; - saw_synth.volume( factor / 31 * v ); - square_synth.volume( factor * 0.5 / 15 * v ); -} - -inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq ) -{ - saw_synth.treble_eq( eq ); - square_synth.treble_eq( eq ); -} - -#endif - diff --git a/quicknes/nes_emu/abstract_file.cpp b/quicknes/nes_emu/abstract_file.cpp deleted file mode 100644 index 48a1b3d1e5..0000000000 --- a/quicknes/nes_emu/abstract_file.cpp +++ /dev/null @@ -1,308 +0,0 @@ - -#include "abstract_file.h" - -#include "blargg_config.h" - -#include -#include -#include - -/* Copyright (C) 2005-2006 Shay Green. Permission is hereby granted, free of -charge, to any person obtaining a copy of this software module 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 AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -// to do: remove? -#ifndef RAISE_ERROR - #define RAISE_ERROR( str ) return str -#endif - -typedef blargg_err_t error_t; - -error_t Data_Writer::write( const void*, long ) { return 0; } - -void Data_Writer::satisfy_lame_linker_() { } - -// Std_File_Writer - -Std_File_Writer::Std_File_Writer() : file_( 0 ) { -} - -Std_File_Writer::~Std_File_Writer() { - close(); -} - -error_t Std_File_Writer::open( const char* path ) -{ - close(); - file_ = fopen( path, "wb" ); - if ( !file_ ) - RAISE_ERROR( "Couldn't open file for writing" ); - - // to do: increase file buffer size - //setvbuf( file_, 0, _IOFBF, 32 * 1024L ); - - return 0; -} - -error_t Std_File_Writer::write( const void* p, long s ) -{ - long result = (long) fwrite( p, 1, s, file_ ); - if ( result != s ) - RAISE_ERROR( "Couldn't write to file" ); - return 0; -} - -void Std_File_Writer::close() -{ - if ( file_ ) { - fclose( file_ ); - file_ = 0; - } -} - -// Mem_Writer - -Mem_Writer::Mem_Writer( void* p, long s, int b ) -{ - data_ = (char*) p; - size_ = 0; - allocated = s; - mode = b ? ignore_excess : fixed; -} - -Mem_Writer::Mem_Writer() -{ - data_ = 0; - size_ = 0; - allocated = 0; - mode = expanding; -} - -Mem_Writer::~Mem_Writer() -{ - if ( mode == expanding ) - free( data_ ); -} - -error_t Mem_Writer::write( const void* p, long s ) -{ - long remain = allocated - size_; - if ( s > remain ) - { - if ( mode == fixed ) - RAISE_ERROR( "Tried to write more data than expected" ); - - if ( mode == ignore_excess ) - { - s = remain; - } - else // expanding - { - long new_allocated = size_ + s; - new_allocated += (new_allocated >> 1) + 2048; - void* p = realloc( data_, new_allocated ); - if ( !p ) - RAISE_ERROR( "Out of memory" ); - data_ = (char*) p; - allocated = new_allocated; - } - } - - assert( size_ + s <= allocated ); - memcpy( data_ + size_, p, s ); - size_ += s; - - return 0; -} - -// Null_Writer - -error_t Null_Writer::write( const void*, long ) -{ - return 0; -} - -// Auto_File_Reader - -#ifndef STD_AUTO_FILE_WRITER - #define STD_AUTO_FILE_WRITER Std_File_Writer -#endif - -#ifdef HAVE_ZLIB_H - #ifndef STD_AUTO_FILE_READER - #define STD_AUTO_FILE_READER Gzip_File_Reader - #endif - - #ifndef STD_AUTO_FILE_COMP_WRITER - #define STD_AUTO_FILE_COMP_WRITER Gzip_File_Writer - #endif - -#else - #ifndef STD_AUTO_FILE_READER - #define STD_AUTO_FILE_READER Std_File_Reader - #endif - - #ifndef STD_AUTO_FILE_COMP_WRITER - #define STD_AUTO_FILE_COMP_WRITER Std_File_Writer - #endif - -#endif - -const char* Auto_File_Reader::open() -{ - #ifdef DISABLE_AUTO_FILE - return 0; - #else - if ( data ) - return 0; - STD_AUTO_FILE_READER* d = new STD_AUTO_FILE_READER; - if ( !d ) - RAISE_ERROR( "Out of memory" ); - data = d; - return d->open( path ); - #endif -} - -Auto_File_Reader::~Auto_File_Reader() -{ - if ( path ) - delete data; -} - -// Auto_File_Writer - -const char* Auto_File_Writer::open() -{ - #ifdef DISABLE_AUTO_FILE - return 0; - #else - if ( data ) - return 0; - STD_AUTO_FILE_WRITER* d = new STD_AUTO_FILE_WRITER; - if ( !d ) - RAISE_ERROR( "Out of memory" ); - data = d; - return d->open( path ); - #endif -} - -const char* Auto_File_Writer::open_comp( int level ) -{ - #ifdef DISABLE_AUTO_FILE - return 0; - #else - if ( data ) - return 0; - STD_AUTO_FILE_COMP_WRITER* d = new STD_AUTO_FILE_COMP_WRITER; - if ( !d ) - RAISE_ERROR( "Out of memory" ); - data = d; - return d->open( path, level ); - #endif -} - -Auto_File_Writer::~Auto_File_Writer() -{ - #ifndef DISABLE_AUTO_FILE - if ( path ) - delete data; - #endif -} - -#ifndef __LIBRETRO__ -#ifdef HAVE_ZLIB_H - -#include "zlib.h" - -static const char* get_gzip_eof( FILE* file, long* eof ) -{ - unsigned char buf [4]; - if ( !fread( buf, 2, 1, file ) ) - RAISE_ERROR( "Couldn't read from file" ); - - if ( buf [0] == 0x1F && buf [1] == 0x8B ) - { - if ( fseek( file, -4, SEEK_END ) ) - RAISE_ERROR( "Couldn't seek in file" ); - - if ( !fread( buf, 4, 1, file ) ) - RAISE_ERROR( "Couldn't read from file" ); - - *eof = buf [3] * 0x1000000L + buf [2] * 0x10000L + buf [1] * 0x100L + buf [0]; - } - else - { - if ( fseek( file, 0, SEEK_END ) ) - RAISE_ERROR( "Couldn't seek in file" ); - - *eof = ftell( file ); - } - - return 0; -} - -const char* get_gzip_eof( const char* path, long* eof ) -{ - FILE* file = fopen( path, "rb" ); - if ( !file ) - return "Couldn't open file"; - const char* error = get_gzip_eof( file, eof ); - fclose( file ); - return error; -} - -// Gzip_File_Writer - -Gzip_File_Writer::Gzip_File_Writer() : file_( 0 ) -{ -} - -Gzip_File_Writer::~Gzip_File_Writer() -{ - close(); -} - -Gzip_File_Writer::error_t Gzip_File_Writer::open( const char* path, int level ) -{ - close(); - - char mode [4] = { 'w', 'b', 0, 0 }; - if ( level >= 0 ) - mode [2] = level + '0'; - file_ = gzopen( path, mode ); - if ( !file_ ) - return "Couldn't open file for writing"; - - return 0; -} - -Gzip_File_Writer::error_t Gzip_File_Writer::write( const void* p, long s ) -{ - long result = (long) gzwrite( (gzFile) file_ , (void*) p, s ); - if ( result != s ) - return "Couldn't write to file"; - return 0; -} - -void Gzip_File_Writer::close() -{ - if ( file_ ) - { - gzclose( (gzFile) file_ ); - file_ = 0; - } -} -#endif - -#endif diff --git a/quicknes/nes_emu/abstract_file.h b/quicknes/nes_emu/abstract_file.h deleted file mode 100644 index a7606c3d9b..0000000000 --- a/quicknes/nes_emu/abstract_file.h +++ /dev/null @@ -1,162 +0,0 @@ - -// Abstract file access interfaces - -#ifndef ABSTRACT_FILE_H -#define ABSTRACT_FILE_H - -#undef BLARGG_CONFIG_H - -#include - -#include - -// Supports writing -class Data_Writer { -public: - Data_Writer() { } - virtual ~Data_Writer() { } - - typedef blargg_err_t error_t; - - // Write 'n' bytes. NULL on success, otherwise error string. - virtual error_t write( const void*, long n ) = 0; - - void satisfy_lame_linker_(); -private: - // noncopyable - Data_Writer( const Data_Writer& ); - Data_Writer& operator = ( const Data_Writer& ); -}; - -class Std_File_Writer : public Data_Writer { -public: - Std_File_Writer(); - ~Std_File_Writer(); - - error_t open( const char* ); - - FILE* file() const { return file_; } - - // Forward writes to file. Caller must close file later. - //void forward( FILE* ); - - error_t write( const void*, long ); - - void close(); - -protected: - void reset( FILE* f ) { file_ = f; } -private: - FILE* file_; - error_t open( const char* path, int ignored ) { return open( path ); } - friend class Auto_File_Writer; -}; - -// Write data to memory -class Mem_Writer : public Data_Writer { - char* data_; - long size_; - long allocated; - enum { expanding, fixed, ignore_excess } mode; -public: - // Keep all written data in expanding block of memory - Mem_Writer(); - - // Write to fixed-size block of memory. If ignore_excess is false, returns - // error if more than 'size' data is written, otherwise ignores any excess. - Mem_Writer( void*, long size, int ignore_excess = 0 ); - - error_t write( const void*, long ); - - // Pointer to beginning of written data - char* data() { return data_; } - - // Number of bytes written - long size() const { return size_; } - - ~Mem_Writer(); -}; - -// Written data is ignored -class Null_Writer : public Data_Writer { -public: - error_t write( const void*, long ); -}; - -// Auto_File to use in place of Data_Reader&/Data_Writer&, allowing a normal -// file path to be used in addition to a Data_Reader/Data_Writer. - -class Auto_File_Reader { -public: - Auto_File_Reader() : data( 0 ), path( 0 ) { } - Auto_File_Reader( Data_Reader& r ) : data( &r ), path( 0 ) { } -#ifndef DISABLE_AUTO_FILE - Auto_File_Reader( const char* path_ ) : data( 0 ), path( path_ ) { } -#endif - Auto_File_Reader( Auto_File_Reader const& ); - Auto_File_Reader& operator = ( Auto_File_Reader const& ); - ~Auto_File_Reader(); - const char* open(); - - int operator ! () const { return !data; } - Data_Reader* operator -> () const { return data; } - Data_Reader& operator * () const { return *data; } -private: - /* mutable */ Data_Reader* data; - const char* path; -}; - -class Auto_File_Writer { -public: - Auto_File_Writer() : data( 0 ), path( 0 ) { } - Auto_File_Writer( Data_Writer& r ) : data( &r ), path( 0 ) { } -#ifndef DISABLE_AUTO_FILE - Auto_File_Writer( const char* path_ ) : data( 0 ), path( path_ ) { } -#endif - Auto_File_Writer( Auto_File_Writer const& ); - Auto_File_Writer& operator = ( Auto_File_Writer const& ); - ~Auto_File_Writer(); - const char* open(); - const char* open_comp( int level = -1 ); // compress output if possible - - int operator ! () const { return !data; } - Data_Writer* operator -> () const { return data; } - Data_Writer& operator * () const { return *data; } -private: - /* mutable */ Data_Writer* data; - const char* path; -}; - -inline Auto_File_Reader& Auto_File_Reader::operator = ( Auto_File_Reader const& r ) -{ - data = r.data; - path = r.path; - ((Auto_File_Reader*) &r)->data = 0; - return *this; -} -inline Auto_File_Reader::Auto_File_Reader( Auto_File_Reader const& r ) { *this = r; } - -inline Auto_File_Writer& Auto_File_Writer::operator = ( Auto_File_Writer const& r ) -{ - data = r.data; - path = r.path; - ((Auto_File_Writer*) &r)->data = 0; - return *this; -} -inline Auto_File_Writer::Auto_File_Writer( Auto_File_Writer const& r ) { *this = r; } - -#ifndef __LIBRETRO__ -class Gzip_File_Writer : public Data_Writer { - void* file_; -public: - Gzip_File_Writer(); - ~Gzip_File_Writer(); - - error_t open( const char*, int compression = -1 ); - error_t write( const void*, long ); - void close(); -}; -#endif - -#endif - diff --git a/quicknes/nes_emu/apu_state.cpp b/quicknes/nes_emu/apu_state.cpp deleted file mode 100644 index 704c02a1a6..0000000000 --- a/quicknes/nes_emu/apu_state.cpp +++ /dev/null @@ -1,132 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "apu_state.h" -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -template -struct apu_reflection -{ - #define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu)) - - static void reflect_env( apu_state_t::env_t* state, Nes_Envelope& osc ) - { - REFLECT( (*state) [0], osc.env_delay ); - REFLECT( (*state) [1], osc.envelope ); - REFLECT( (*state) [2], osc.reg_written [3] ); - } - - static void reflect_square( apu_state_t::square_t& state, Nes_Square& osc ) - { - reflect_env( &state.env, osc ); - REFLECT( state.delay, osc.delay ); - REFLECT( state.length_counter, osc.length_counter ); - REFLECT( state.phase, osc.phase ); - REFLECT( state.swp_delay, osc.sweep_delay ); - REFLECT( state.swp_reset, osc.reg_written [1] ); - } - - static void reflect_triangle( apu_state_t::triangle_t& state, Nes_Triangle& osc ) - { - REFLECT( state.delay, osc.delay ); - REFLECT( state.length_counter, osc.length_counter ); - REFLECT( state.linear_counter, osc.linear_counter ); - REFLECT( state.linear_mode, osc.reg_written [3] ); - } - - static void reflect_noise( apu_state_t::noise_t& state, Nes_Noise& osc ) - { - reflect_env( &state.env, osc ); - REFLECT( state.delay, osc.delay ); - REFLECT( state.length_counter, osc.length_counter ); - REFLECT( state.shift_reg, osc.noise ); - } - - static void reflect_dmc( apu_state_t::dmc_t& state, Nes_Dmc& osc ) - { - REFLECT( state.delay, osc.delay ); - REFLECT( state.remain, osc.length_counter ); - REFLECT( state.buf, osc.buf ); - REFLECT( state.bits_remain, osc.bits_remain ); - REFLECT( state.bits, osc.bits ); - REFLECT( state.buf_full, osc.buf_full ); - REFLECT( state.silence, osc.silence ); - REFLECT( state.irq_flag, osc.irq_flag ); - if ( mode ) - state.addr = osc.address | 0x8000; - else - osc.address = state.addr & 0x7fff; - } -}; - -void Nes_Apu::save_state( apu_state_t* state ) const -{ - for ( int i = 0; i < osc_count * 4; i++ ) - { - int index = i >> 2; - state->apu.w40xx [i] = oscs [index]->regs [i & 3]; - //if ( index < 4 ) - // state->length_counters [index] = oscs [index]->length_counter; - } - state->apu.w40xx [0x11] = dmc.dac; - - state->apu.w4015 = osc_enables; - state->apu.w4017 = frame_mode; - state->apu.frame_delay = frame_delay; - state->apu.frame_step = frame; - state->apu.irq_flag = irq_flag; - - typedef apu_reflection<1> refl; - Nes_Apu& apu = *(Nes_Apu*) this; // const_cast - refl::reflect_square ( state->square1, apu.square1 ); - refl::reflect_square ( state->square2, apu.square2 ); - refl::reflect_triangle( state->triangle, apu.triangle ); - refl::reflect_noise ( state->noise, apu.noise ); - refl::reflect_dmc ( state->dmc, apu.dmc ); -} - -void Nes_Apu::load_state( apu_state_t const& state ) -{ - reset(); - - write_register( 0, 0x4017, state.apu.w4017 ); - write_register( 0, 0x4015, state.apu.w4015 ); - osc_enables = state.apu.w4015; // DMC clears bit 4 - - for ( int i = 0; i < osc_count * 4; i++ ) - { - int n = state.apu.w40xx [i]; - int index = i >> 2; - oscs [index]->regs [i & 3] = n; - write_register( 0, 0x4000 + i, n ); - //if ( index < 4 ) - // oscs [index]->length_counter = state.length_counters [index]; - } - - frame_delay = state.apu.frame_delay; - frame = state.apu.frame_step; - irq_flag = state.apu.irq_flag; - - typedef apu_reflection<0> refl; - apu_state_t& st = (apu_state_t&) state; // const_cast - refl::reflect_square ( st.square1, square1 ); - refl::reflect_square ( st.square2, square2 ); - refl::reflect_triangle( st.triangle, triangle ); - refl::reflect_noise ( st.noise, noise ); - refl::reflect_dmc ( st.dmc, dmc ); - dmc.recalc_irq(); -} - diff --git a/quicknes/nes_emu/apu_state.h b/quicknes/nes_emu/apu_state.h deleted file mode 100644 index a2ba3ffd76..0000000000 --- a/quicknes/nes_emu/apu_state.h +++ /dev/null @@ -1,79 +0,0 @@ - -// NES APU state snapshot support - -// Nes_Snd_Emu 0.1.7 - -#ifndef APU_STATE_H -#define APU_STATE_H - -#include "blargg_common.h" - -struct apu_state_t -{ - typedef BOOST::uint8_t byte; - - typedef byte env_t [3]; - /*struct env_t { - byte delay; - byte env; - byte written; - };*/ - - struct apu_t { - byte w40xx [0x14]; // $4000-$4013 - byte w4015; // enables - byte w4017; // mode - BOOST::uint16_t frame_delay; - byte frame_step; - byte irq_flag; - } apu; - - struct square_t { - BOOST::uint16_t delay; - env_t env; - byte length_counter; - byte phase; - byte swp_delay; - byte swp_reset; - byte unused2 [1]; - }; - - square_t square1; - square_t square2; - - struct triangle_t { - BOOST::uint16_t delay; - byte length_counter; - byte phase; - byte linear_counter; - byte linear_mode; - } triangle; - - struct noise_t { - BOOST::uint16_t delay; - env_t env; - byte length_counter; - BOOST::uint16_t shift_reg; - } noise; - - struct dmc_t { - BOOST::uint16_t delay; - BOOST::uint16_t remain; - BOOST::uint16_t addr; - byte buf; - byte bits_remain; - byte bits; - byte buf_full; - byte silence; - byte irq_flag; - } dmc; - - //byte length_counters [4]; - - enum { tag = 0x41505552 }; // 'APUR' - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (apu_state_t) == 72 ); - -#endif - diff --git a/quicknes/nes_emu/blargg_common.h b/quicknes/nes_emu/blargg_common.h deleted file mode 100644 index ca7f38216e..0000000000 --- a/quicknes/nes_emu/blargg_common.h +++ /dev/null @@ -1,164 +0,0 @@ - -// Sets up common environment for Shay Green's libraries. -// -// To change configuration options, modify blargg_config.h, not this file. - -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -#include -#include -#include -#include - -// User configuration (allow it to #include "blargg_common.h" normally) -#undef BLARGG_COMMON_H -#include "blargg_config.h" -#define BLARGG_COMMON_H - -/* BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, - compiler is assumed to support bool. If undefined, availability is determined. - If errors occur here, add the following line to your config.h file: - #define BLARGG_COMPILER_HAS_BOOL 1 -*/ -#ifndef BLARGG_COMPILER_HAS_BOOL - #if defined (__MWERKS__) - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (_MSC_VER) - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (__GNUC__) - // supports bool - #elif __cplusplus < 199711 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif -#endif -#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// BLARGG_NEW is used in place of 'new' to create objects. By default, plain new is used. -// To prevent an exception if out of memory, #define BLARGG_NEW new (std::nothrow) -#ifndef BLARGG_NEW - #define BLARGG_NEW new -#endif - -// BOOST::int8_t etc. - -// HAVE_STDINT_H: If defined, use for int8_t etc. -#if defined (HAVE_STDINT_H) - #include - #define BOOST - -// HAVE_INTTYPES_H: If defined, use for int8_t etc. -#elif defined (HAVE_INTTYPES_H) - #include - #define BOOST - -#else - struct BOOST - { - #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F - typedef signed char int8_t; - typedef unsigned char uint8_t; - #else - // No suitable 8-bit type available - typedef struct see_blargg_common_h int8_t; - typedef struct see_blargg_common_h uint8_t; - #endif - - #if USHRT_MAX == 0xFFFF - typedef short int16_t; - typedef unsigned short uint16_t; - #else - // No suitable 16-bit type available - typedef struct see_blargg_common_h int16_t; - typedef struct see_blargg_common_h uint16_t; - #endif - - #if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; - #elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; - #else - // No suitable 32-bit type available - typedef struct see_blargg_common_h int32_t; - typedef struct see_blargg_common_h uint32_t; - #endif - }; -#endif - -#ifdef _WIN32 -typedef wchar_t blargg_wchar_t; -#else -#include -typedef uint16_t blargg_wchar_t; -#endif - -// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -#ifndef BOOST_STATIC_ASSERT - #ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] ) - #else - // Some other compilers fail when declaring same function multiple times in class, - // so differentiate them by line - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) - #endif -#endif - -// In case compiler doesn't support these properly. Used rarely. -#define STATIC_CAST(T,expr) static_cast (expr) -#define CONST_CAST( T,expr) const_cast (expr) - -// blargg_err_t (0 on success, otherwise error string) -#ifndef blargg_err_t - typedef const char* blargg_err_t; -#endif - -// Success; no error -blargg_err_t const blargg_ok = 0; - -/* Pure virtual functions cause a vtable entry to a "called pure virtual" -error handler, requiring linkage to the C++ runtime library. This macro is -used in place of the "= 0", and simply expands to its argument. During -development, it expands to "= 0", allowing detection of missing overrides. */ -#define BLARGG_PURE( def ) def - -/* My code is not written with exceptions in mind, so either uses new (nothrow) -OR overrides operator new in my classes. The former is best since clients -creating objects will get standard exceptions on failure, but that causes it -to require the standard C++ library. So, when the client is using the C -interface, I override operator new to use malloc. */ - -// BLARGG_DISABLE_NOTHROW is put inside classes -#ifndef BLARGG_DISABLE_NOTHROW - // throw spec mandatory in ISO C++ if NULL can be returned - #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 - #define BLARGG_THROWS_NOTHING throw () - #else - #define BLARGG_THROWS_NOTHING - #endif - - #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ - void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } - - #define BLARGG_NEW new -#else - // BLARGG_NEW is used in place of new in library code - #include - #define BLARGG_NEW new (std::nothrow) -#endif - -#endif - diff --git a/quicknes/nes_emu/blargg_config.h b/quicknes/nes_emu/blargg_config.h deleted file mode 100644 index f4d7b83977..0000000000 --- a/quicknes/nes_emu/blargg_config.h +++ /dev/null @@ -1,33 +0,0 @@ - -// Nes_Emu 0.7.0 user configuration file. Don't replace when updating library. - -#ifndef BLARGG_CONFIG_H -#define BLARGG_CONFIG_H - -// Uncomment to transparently decompress files using zlib -#define HAVE_ZLIB_H - -// Uncomment to enable platform-specific (and possibly non-portable) optimizations. -#define BLARGG_NONPORTABLE 1 - -// Uncomment if automatic byte-order determination doesn't work -//#define BLARGG_BIG_ENDIAN 1 - -// Uncomment if you get errors in the bool section of blargg_common.h -//#define BLARGG_COMPILER_HAS_BOOL 1 - -// Uncomment to disable out-of-memory exceptions -//#include -//#define BLARGG_NEW new (std::nothrow) - -#define DISABLE_AUTO_FILE 1 - -#define HAVE_STDINT_H - -// Use standard config.h if present -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#endif - diff --git a/quicknes/nes_emu/blargg_endian.h b/quicknes/nes_emu/blargg_endian.h deleted file mode 100644 index 5f75734811..0000000000 --- a/quicknes/nes_emu/blargg_endian.h +++ /dev/null @@ -1,159 +0,0 @@ - -// CPU Byte Order Utilities - -// Nes_Emu 0.7.0 - -#ifndef BLARGG_ENDIAN -#define BLARGG_ENDIAN - -#include "blargg_common.h" - -// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) -#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 1 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one must be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__mips__) || defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#else - // Assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN -#endif - -inline void blargg_verify_byte_order() -{ -#ifndef NDEBUG - #if BLARGG_BIG_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i == 0 ); - #elif BLARGG_LITTLE_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i != 0 ); - #endif -#endif -} - -inline unsigned get_le16( void const* p ) { - return ((unsigned char*) p) [1] * 0x100u + - ((unsigned char*) p) [0]; -} -inline unsigned get_be16( void const* p ) { - return ((unsigned char*) p) [0] * 0x100u + - ((unsigned char*) p) [1]; -} -inline unsigned long get_le32( void const* p ) { - return ((unsigned char*) p) [3] * 0x01000000ul + - ((unsigned char*) p) [2] * 0x00010000ul + - ((unsigned char*) p) [1] * 0x00000100ul + - ((unsigned char*) p) [0]; -} -inline unsigned long get_be32( void const* p ) { - return ((unsigned char*) p) [0] * 0x01000000ul + - ((unsigned char*) p) [1] * 0x00010000ul + - ((unsigned char*) p) [2] * 0x00000100ul + - ((unsigned char*) p) [3]; -} -inline void set_le16( void* p, unsigned n ) { - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be16( void* p, unsigned n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} -inline void set_le32( void* p, unsigned long n ) { - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be32( void* p, unsigned long n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [3] = (unsigned char) n; -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #endif - - #if BLARGG_CPU_POWERPC && defined (__MWERKS__) - // PowerPC has special byte-reversed instructions - // to do: assumes that PowerPC is running in big-endian mode - // to do: implement for other compilers which don't support these macros - #define GET_LE16( addr ) (__lhbrx( (addr), 0 )) - #define GET_LE32( addr ) (__lwbrx( (addr), 0 )) - #define SET_LE16( addr, data ) (__sthbrx( (data), (addr), 0 )) - #define SET_LE32( addr, data ) (__stwbrx( (data), (addr), 0 )) - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, unsigned long n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, unsigned long n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline unsigned long get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline unsigned long get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } - -#endif - diff --git a/quicknes/nes_emu/blargg_source.h b/quicknes/nes_emu/blargg_source.h deleted file mode 100644 index 3bd328d9ea..0000000000 --- a/quicknes/nes_emu/blargg_source.h +++ /dev/null @@ -1,76 +0,0 @@ - -// Included at the beginning of library source files, after all other #include lines - -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert( expr ) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -inline void blargg_dprintf_( const char*, ... ) { } -#undef dprintf -#define dprintf (1) ? (void) 0 : blargg_dprintf_ - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#define check( expr ) ((void) 0) - -// If expr yields error string, return it from current function, otherwise continue. -#undef RETURN_ERR -#define RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is 0, return out of memory error string. -#undef CHECK_ALLOC -#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) - -// Avoid any macros which evaluate their arguments multiple times -#undef min -#undef max - -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -template -inline T min( T x, T y ) -{ - if ( x < y ) - return x; - return y; -} - -template -inline T max( T x, T y ) -{ - if ( x < y ) - return y; - return x; -} - -// deprecated -#define BLARGG_CHECK_ALLOC CHECK_ALLOC -#define BLARGG_RETURN_ERR RETURN_ERR - -// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check -#ifdef BLARGG_SOURCE_BEGIN - #include BLARGG_SOURCE_BEGIN -#endif - -#endif - diff --git a/quicknes/nes_emu/misc_mappers.cpp b/quicknes/nes_emu/misc_mappers.cpp deleted file mode 100644 index 587fb6f8ed..0000000000 --- a/quicknes/nes_emu/misc_mappers.cpp +++ /dev/null @@ -1,348 +0,0 @@ - -// Optional less-common simple mappers - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -// Nina-1 (Deadly Towers only) - -class Mapper_Nina1 : public Nes_Mapper { - byte bank; -public: - Mapper_Nina1() - { - register_state( &bank, 1 ); - } - - virtual void apply_mapping() - { - write( 0, 0, bank ); - } - - virtual void write( nes_time_t, nes_addr_t, int data ) - { - bank = data; - set_prg_bank( 0x8000, bank_32k, bank ); - } -}; - -// GNROM - -class Mapper_Gnrom : public Nes_Mapper { - byte bank; -public: - Mapper_Gnrom() - { - register_state( &bank, 1 ); - } - - virtual void apply_mapping() - { - int b = bank; - bank = ~b; - write( 0, 0, b ); - } - - virtual void write( nes_time_t, nes_addr_t, int data ) - { - int changed = bank ^ data; - bank = data; - - if ( changed & 0x30 ) - set_prg_bank( 0x8000, bank_32k, bank >> 4 & 3 ); - - if ( changed & 0x03 ) - set_chr_bank( 0, bank_8k, bank & 3 ); - } -}; - -// Color Dreams - -class Mapper_Color_Dreams : public Nes_Mapper { - byte bank; -public: - Mapper_Color_Dreams() - { - register_state( &bank, 1 ); - } - - virtual void apply_mapping() - { - int b = bank; - bank = ~b; - write( 0, 0, b ); - } - - virtual void write( nes_time_t, nes_addr_t, int data ) - { - int changed = bank ^ data; - bank = data; - - if ( changed & 0x0f ) - set_prg_bank( 0x8000, bank_32k, bank & 0x0f ); - - if ( changed & 0xf0 ) - set_chr_bank( 0, bank_8k, bank >> 4 ); - } -}; - -// Jaleco/Konami - -class Mapper_87 : public Nes_Mapper { - byte bank; -public: - Mapper_87() - { - register_state( &bank, 1 ); - } - - void apply_mapping() - { - intercept_writes( 0x6000, 1 ); - write( 0, 0x6000, bank ); - } - - bool write_intercepted( nes_time_t, nes_addr_t addr, int data ) - { - if ( addr != 0x6000 ) - return false; - - bank = data; - set_chr_bank( 0, bank_8k, data >> 1 ); - return true; - } - - void write( nes_time_t, nes_addr_t, int ) { } -}; - -// Camerica - -class Mapper_Camerica : public Nes_Mapper { - byte regs [3]; -public: - Mapper_Camerica() - { - register_state( regs, sizeof regs ); - } - - virtual void apply_mapping() - { - write( 0, 0xc000, regs [0] ); - if ( regs [1] & 0x80 ) - write( 0, 0x9000, regs [1] ); - } - - virtual void write( nes_time_t, nes_addr_t addr, int data ) - { - if ( addr >= 0xc000 ) - { - regs [0] = data; - set_prg_bank( 0x8000, bank_16k, data ); - } - else if ( (addr & 0xf000) == 0x9000 ) - { - regs [1] = 0x80 | data; - mirror_single( (data >> 4) & 1 ); - } - } -}; - -// Quattro - -class Mapper_Quattro : public Nes_Mapper { - byte regs [2]; -public: - Mapper_Quattro() - { - register_state( regs, sizeof regs ); - } - - virtual void reset_state() - { - regs [0] = 0; - regs [1] = 3; - } - - virtual void apply_mapping() - { - int bank = regs [0] >> 1 & 0x0c; - set_prg_bank( 0x8000, bank_16k, bank + (regs [1] & 3) ); - set_prg_bank( 0xC000, bank_16k, bank + 3 ); - } - - virtual void write( nes_time_t, nes_addr_t addr, int data ) - { - if ( addr < 0xc000 ) - regs [0] = data; - else - regs [1] = data; - Mapper_Quattro::apply_mapping(); - } -}; - -class Mapper_78 : public Nes_Mapper { - // lower 8 bits are the reg at 8000:ffff - // next two bits are autodetecting type - // 0 = unknown 1 = cosmo carrier 2 = holy diver - int reg; - void writeinternal(int data, int changed) - { - reg &= 0x300; - reg |= data; - - if (changed & 0x07) - set_prg_bank(0x8000, bank_16k, reg & 0x07); - if (changed & 0xf0) - set_chr_bank(0x0000, bank_8k, (reg >> 4) & 0x0f); - if (changed & 0x08) - { - // set mirroring based on memorized board type - if (reg & 0x100) - { - mirror_single((reg >> 3) & 1); - } - else if (reg & 0x200) - { - if (reg & 0x08) - mirror_vert(); - else - mirror_horiz(); - } - else - { - // if you don't set something here, holy diver dumps with 4sc set will - // savestate as 4k NTRAM. then when you later set H\V mapping, state size mismatch. - mirror_single(1); - } - } - } - -public: - Mapper_78() - { - register_state(®, 4); - } - - virtual void reset_state() - { - reg = 0; - } - - virtual void apply_mapping() - { - writeinternal(reg, 0xff); - } - - virtual void write( nes_time_t, nes_addr_t addr, int data) - { - // heuristic: if the first write ever to the register is 0, - // we're on holy diver, otherwise, carrier. it works for these two games... - if (!(reg & 0x300)) - { - reg |= data ? 0x100 : 0x200; - writeinternal(data, 0xff); - } - else - { - writeinternal(data, reg ^ data); - } - } -}; - -class Mapper_212 : public Nes_Mapper { - // something about this doesn't work right? - // TODO: fix me - int reg; - void writeinternal(int data, int changed) - { - reg = data; - - if (changed & 0x0007) - { - set_chr_bank(0x0000, bank_8k, reg & 0x0007); - } - if (changed & 0x0008) - { - if (reg & 0x0008) - { - mirror_horiz(); - } - else - { - mirror_vert(); - } - } - if (changed & 0x4007) - { - if (reg & 0x4000) - { - set_prg_bank(0x8000, bank_32k, reg >> 1 & 0x0003); - } - else - { - set_prg_bank(0x8000, bank_16k, reg & 0x0007); - set_prg_bank(0xc000, bank_16k, reg & 0x0007); - } - } - } - -public: - Mapper_212() - { - register_state(®, 4); - } - - virtual void reset_state() - { - reg = 0xffff; - } - - virtual void apply_mapping() - { - //intercept_reads(0xe000, 0x2000); // some sort of bus conflict or anti-piracy bs - writeinternal(reg, 0xffff); - } - /* - // as written. this will never be hit. what's going on? - virtual int read( nes_time_t time, nes_addr_t addr ) - { - int ret = Nes_Mapper::read(time, addr); - if ((addr & 0xe010) == 0x6000) - ret |= 0x80; - return ret; - }*/ - - virtual void write( nes_time_t, nes_addr_t addr, int data) - { - writeinternal(addr, addr ^ reg); - } -}; - -void register_misc_mappers(); -void register_misc_mappers() -{ - register_mapper( 11 ); - register_mapper( 34 ); - register_mapper( 66 ); - register_mapper( 71 ); - register_mapper( 87 ); - register_mapper( 232 ); - - register_mapper( 78 ); - // register_mapper( 212 ); -} - diff --git a/quicknes/nes_emu/nes_cpu_io.h b/quicknes/nes_emu/nes_cpu_io.h deleted file mode 100644 index e09e0f45d9..0000000000 --- a/quicknes/nes_emu/nes_cpu_io.h +++ /dev/null @@ -1,144 +0,0 @@ - -#include "Nes_Core.h" -#include "Nes_Mapper.h" - -#include "blargg_source.h" - -int Nes_Core::cpu_read( nes_addr_t addr, nes_time_t time ) -{ - //LOG_FREQ( "cpu_read", 16, addr >> 12 ); - - { - int result = cpu::low_mem [addr & 0x7FF]; - if ( !(addr & 0xE000) ) - return result; - } - - { - int result = *cpu::get_code( addr ); - if ( addr > 0x7FFF ) - return result; - } - - time += cpu_time_offset; - if ( addr < 0x4000 ) - return ppu.read( addr, time ); - - clock_ = time; - if ( data_reader_mapped [addr >> page_bits] ) - { - int result = mapper->read( time, addr ); - if ( result >= 0 ) - return result; - } - - if ( addr < 0x6000 ) - return read_io( addr ); - - if ( addr < sram_readable ) - return impl->sram [addr & (impl_t::sram_size - 1)]; - - if ( addr < lrom_readable ) - return *cpu::get_code( addr ); - - #ifndef NDEBUG - log_unmapped( addr ); - #endif - - return addr >> 8; // simulate open bus -} - -inline int Nes_Core::cpu_read_ppu( nes_addr_t addr, nes_time_t time ) -{ - //LOG_FREQ( "cpu_read_ppu", 16, addr >> 12 ); - - // Read of status register (0x2002) is heavily optimized since many games - // poll it hundreds of times per frame. - nes_time_t next = ppu_2002_time; - int result = ppu.r2002; - if ( addr == 0x2002 ) - { - ppu.second_write = false; - if ( time >= next ) - result = ppu.read_2002( time + cpu_time_offset ); - } - else - { - result = cpu::low_mem [addr & 0x7FF]; - if ( addr >= 0x2000 ) - result = cpu_read( addr, time ); - } - - return result; -} - -void Nes_Core::cpu_write_2007( int data ) -{ - // ppu.write_2007() is inlined - if ( ppu.write_2007( data ) & Nes_Ppu::vaddr_clock_mask ) - mapper->a12_clocked(); -} - -void Nes_Core::cpu_write( nes_addr_t addr, int data, nes_time_t time ) -{ - //LOG_FREQ( "cpu_write", 16, addr >> 12 ); - - if ( !(addr & 0xE000) ) - { - cpu::low_mem [addr & 0x7FF] = data; - return; - } - - time += cpu_time_offset; - if ( addr < 0x4000 ) - { - if ( (addr & 7) == 7 ) - cpu_write_2007( data ); - else - ppu.write( time, addr, data ); - return; - } - - clock_ = time; - if ( data_writer_mapped [addr >> page_bits] && mapper->write_intercepted( time, addr, data ) ) - return; - - if ( addr < 0x6000 ) - { - write_io( addr, data ); - return; - } - - if ( addr < sram_writable ) - { - impl->sram [addr & (impl_t::sram_size - 1)] = data; - return; - } - - if ( addr > 0x7FFF ) - { - mapper->write( clock_, addr, data ); - return; - } - - #ifndef NDEBUG - log_unmapped( addr, data ); - #endif -} - -#define NES_CPU_READ_PPU( cpu, addr, time ) \ - STATIC_CAST(Nes_Core&,*cpu).cpu_read_ppu( addr, time ) - -#define NES_CPU_READ( cpu, addr, time ) \ - STATIC_CAST(Nes_Core&,*cpu).cpu_read( addr, time ) - -#define NES_CPU_WRITEX( cpu, addr, data, time ){\ - STATIC_CAST(Nes_Core&,*cpu).cpu_write( addr, data, time );\ -} - -#define NES_CPU_WRITE( cpu, addr, data, time ){\ - if ( addr < 0x800 ) cpu->low_mem [addr] = data;\ - else if ( addr == 0x2007 ) STATIC_CAST(Nes_Core&,*cpu).cpu_write_2007( data );\ - else STATIC_CAST(Nes_Core&,*cpu).cpu_write( addr, data, time );\ -} - diff --git a/quicknes/nes_emu/nes_data.cpp b/quicknes/nes_emu/nes_data.cpp deleted file mode 100644 index 50e1c91121..0000000000 --- a/quicknes/nes_emu/nes_data.cpp +++ /dev/null @@ -1,75 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "nes_data.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -#define SWAP_BE( n ) (void) (set_be( &(n), (n) )) -#define SWAP_LE( n ) (void) (set_le( &(n), (n) )) - -void nes_block_t::swap() -{ - SWAP_BE( tag ); - SWAP_LE( size ); -} - -void nes_state_t::swap() -{ - SWAP_LE( timestamp ); - SWAP_LE( frame_count ); -} - -void cpu_state_t::swap() -{ - SWAP_LE( pc ); -} - -void ppu_state_t::swap() -{ - SWAP_LE( vram_addr ); - SWAP_LE( vram_temp ); - SWAP_LE( decay_low ); - SWAP_LE( decay_high ); -} - -void apu_state_t::swap() -{ - SWAP_LE( apu.frame_delay ); - SWAP_LE( square1.delay ); - SWAP_LE( square2.delay ); - SWAP_LE( triangle.delay ); - SWAP_LE( noise.delay ); - SWAP_LE( noise.shift_reg ); - SWAP_LE( dmc.delay ); - SWAP_LE( dmc.remain ); - SWAP_LE( dmc.addr ); -} - -void joypad_state_t::swap() -{ - SWAP_LE( joypad_latches [0] ); - SWAP_LE( joypad_latches [1] ); -} - -void movie_info_t::swap() -{ - SWAP_LE( begin ); - SWAP_LE( length ); - SWAP_LE( period ); - SWAP_LE( extra ); -} - diff --git a/quicknes/nes_emu/nes_data.h b/quicknes/nes_emu/nes_data.h deleted file mode 100644 index e831866952..0000000000 --- a/quicknes/nes_emu/nes_data.h +++ /dev/null @@ -1,174 +0,0 @@ - -// NES data file block formats - -// Nes_Emu 0.7.0 - -#ifndef NES_DATA_H -#define NES_DATA_H - -#include "blargg_common.h" -#include "apu_state.h" - -typedef long nes_tag_t; - -#if 'ABCD' == '\101\102\103\104' -#define FOUR_CHAR( c ) (\ - ((c) / '\1\0\0\0' % 0x100 * 0x01000000L) +\ - ((c) / '\0\1\0\0' % 0x100 * 0x00010000L) +\ - ((c) / '\0\0\1\0' % 0x100 * 0x00000100L) +\ - ((c) / '\0\0\0\1' % 0x100 * 0x00000001L)\ -) -#else -#if 'ABCD' == 0x41424344 -#define FOUR_CHAR( c ) c -#else -#define FOUR_CHAR( c ) (\ - ((c) / 0x01000000 % 0x100 * 0x00000001) +\ - ((c) / 0x00010000 % 0x100 * 0x00000100) +\ - ((c) / 0x00000100 % 0x100 * 0x00010000) +\ - ((c) / 0x00000001 % 0x100 * 0x01000000)\ -) -#endif -#endif - -typedef BOOST::uint8_t byte; - -// Binary format of save state blocks. All multi-byte values are stored in little-endian. - -nes_tag_t const state_file_tag = FOUR_CHAR('NESS'); - -nes_tag_t const movie_file_tag = FOUR_CHAR('NMOV'); - -// Name of cartridge file in 8-bit characters (UTF-8 preferred) with ".nes" etc *removed*, -// no NUL termination. Yes: "Castlevania (U)". No: "Strider (U).nes". -nes_tag_t const cart_name_tag = FOUR_CHAR('romn'); - -// CRC-32 of cartridge's PRG and CHR data combined -nes_tag_t const cart_checksum_tag = FOUR_CHAR('csum'); - -struct nes_block_t -{ - BOOST::uint32_t tag; // ** stored in big-endian - BOOST::uint32_t size; - - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (nes_block_t) == 8 ); - -unsigned long const group_begin_size = 0xffffffff; // group block has this size -nes_tag_t const group_end_tag = FOUR_CHAR('gend'); // group end block has this tag - -struct movie_info_t -{ - BOOST::uint32_t begin; - BOOST::uint32_t length; - BOOST::uint16_t period; - BOOST::uint16_t extra; - byte joypad_count; - byte has_joypad_sync; - byte unused [2]; - - enum { tag = FOUR_CHAR('INFO') }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (movie_info_t) == 16 ); - -struct nes_state_t -{ - BOOST::uint16_t timestamp; // CPU clocks * 15 (for NTSC) - byte pal; - byte unused [1]; - BOOST::uint32_t frame_count; // number of frames emulated since power-up - - enum { tag = FOUR_CHAR('TIME') }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (nes_state_t) == 8 ); - -struct joypad_state_t -{ - uint32_t joypad_latches [2]; // joypad 1 & 2 shift registers - byte w4016; // strobe - byte unused [3]; - - enum { tag = FOUR_CHAR('CTRL') }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (joypad_state_t) == 12 ); - -// Increase this (and let me know) if your mapper requires more state. This only -// sets the size of the in-memory buffer; it doesn't affect the file format at all. -unsigned const max_mapper_state_size = 256; -struct mapper_state_t -{ - int size; - union { - double align; - byte data [max_mapper_state_size]; - }; - - void write( const void* p, unsigned long s ); - int read( void* p, unsigned long s ) const; -}; - -struct cpu_state_t -{ - BOOST::uint16_t pc; - byte s; - byte p; - byte a; - byte x; - byte y; - byte unused [1]; - - enum { tag = FOUR_CHAR('CPUR') }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (cpu_state_t) == 8 ); - -struct ppu_state_t -{ - byte w2000; // control - byte w2001; // control - byte r2002; // status - byte w2003; // sprite ram addr - byte r2007; // vram read buffer - byte second_write; // next write to $2005/$2006 is second since last $2002 read - BOOST::uint16_t vram_addr; // loopy_v - BOOST::uint16_t vram_temp; // loopy_t - byte pixel_x; // fine-scroll (0-7) - byte unused; - byte palette [0x20]; // entries $10, $14, $18, $1c should be ignored - BOOST::uint16_t decay_low; - BOOST::uint16_t decay_high; - byte open_bus; - byte unused2[3]; - - enum { tag = FOUR_CHAR('PPUR') }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (ppu_state_t) == 20 + 0x20 ); - -struct mmc1_state_t -{ - byte regs [4]; // current registers (5 bits each) - byte bit; // number of bits in buffer (0 to 4) - byte buf; // currently buffered bits (new bits added to bottom) -}; -BOOST_STATIC_ASSERT( sizeof (mmc1_state_t) == 6 ); - -struct mmc3_state_t -{ - byte banks [8]; // last writes to $8001 indexed by (mode & 7) - byte mode; // $8000 - byte mirror; // $a000 - byte sram_mode; // $a001 - byte irq_ctr; // internal counter - byte irq_latch; // $c000 - byte irq_enabled;// last write was to 0) $e000, 1) $e001 - byte irq_flag; -}; -BOOST_STATIC_ASSERT( sizeof (mmc3_state_t) == 15 ); - -#endif - diff --git a/quicknes/nes_emu/nes_mappers.cpp b/quicknes/nes_emu/nes_mappers.cpp deleted file mode 100644 index 264893bc0a..0000000000 --- a/quicknes/nes_emu/nes_mappers.cpp +++ /dev/null @@ -1,117 +0,0 @@ - -// Common simple mappers - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "Nes_Mapper.h" - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -// NROM - -class Mapper_Nrom : public Nes_Mapper { -public: - Mapper_Nrom() { } - - virtual void apply_mapping() { } - - virtual void write( nes_time_t, nes_addr_t, int ) - { - // empty - } -}; - -Nes_Mapper* Nes_Mapper::make_nrom() { return new Mapper_Nrom; } - -// UNROM - -class Mapper_Unrom : public Nes_Mapper { - byte bank; -public: - Mapper_Unrom() - { - register_state( &bank, 1 ); - } - - virtual void apply_mapping() - { - enable_sram(); // at least one UNROM game needs sram (Bomberman 2) - set_prg_bank( 0x8000, bank_16k, bank ); - } - - virtual void write( nes_time_t, nes_addr_t addr, int data ) - { - bank = handle_bus_conflict( addr, data ); - set_prg_bank( 0x8000, bank_16k, bank ); - } -}; - -Nes_Mapper* Nes_Mapper::make_unrom() { return new Mapper_Unrom; } - -// AOROM - -class Mapper_Aorom : public Nes_Mapper { - byte bank; -public: - Mapper_Aorom() - { - register_state( &bank, 1 ); - } - - virtual void apply_mapping() - { - int b = bank; - bank = ~b; // force update - write( 0, 0, b ); - } - - virtual void write( nes_time_t, nes_addr_t, int data ) - { - int changed = bank ^ data; - bank = data; - - if ( changed & 0x10 ) - mirror_single( bank >> 4 & 1 ); - - if ( changed & 0x0f ) - set_prg_bank( 0x8000, bank_32k, bank & 7 ); - } -}; - -Nes_Mapper* Nes_Mapper::make_aorom() { return new Mapper_Aorom; } - -// CNROM - -class Mapper_Cnrom : public Nes_Mapper { - byte bank; -public: - Mapper_Cnrom() - { - register_state( &bank, 1 ); - } - - virtual void apply_mapping() - { - set_chr_bank( 0, bank_8k, bank & 7 ); - } - - virtual void write( nes_time_t, nes_addr_t addr, int data ) - { - bank = handle_bus_conflict( addr, data ); - set_chr_bank( 0, bank_8k, bank & 7 ); - } -}; - -Nes_Mapper* Nes_Mapper::make_cnrom() { return new Mapper_Cnrom; } - diff --git a/quicknes/nes_emu/nes_ntsc.h b/quicknes/nes_emu/nes_ntsc.h deleted file mode 100644 index 2edeed11b9..0000000000 --- a/quicknes/nes_emu/nes_ntsc.h +++ /dev/null @@ -1,199 +0,0 @@ - -/* NES NTSC video filter */ - -/* nes_ntsc 0.2.0 */ - -#ifndef NES_NTSC_H -#define NES_NTSC_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown -in parenthesis and should remain fairly stable in future versions. */ -typedef struct nes_ntsc_setup_t -{ - /* Basic parameters */ - double hue; /* -1 = -180 degrees +1 = +180 degrees */ - double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ - double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ - double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ - double sharpness; /* edge contrast enhancement/blurring */ - - /* Advanced parameters */ - double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ - double resolution; /* image resolution */ - double artifacts; /* artifacts caused by color changes */ - double fringing; /* color artifacts caused by brightness changes */ - double bleed; /* color bleed (color resolution reduction) */ - int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ - - unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ -} nes_ntsc_setup_t; - -/* Video format presets */ -extern nes_ntsc_setup_t const nes_ntsc_composite; /* color bleeding + artifacts */ -extern nes_ntsc_setup_t const nes_ntsc_svideo; /* color bleeding only */ -extern nes_ntsc_setup_t const nes_ntsc_rgb; /* crisp image */ -extern nes_ntsc_setup_t const nes_ntsc_monochrome;/* desaturated + artifacts */ - -enum { nes_ntsc_palette_size = 64 }; -enum { nes_ntsc_emph_palette_size = 64 * 8 }; - -/* Initialize and adjust parameters. Can be called multiple times on the same -nes_ntsc_t object. Can pass 0 for either parameter. */ -typedef struct nes_ntsc_t nes_ntsc_t; -void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup ); - -/* Filter one or more rows of pixels. Input pixels are 6-bit palette indicies. -In_row_width is the number of pixels to get to the next input row. Out_pitch -is the number of *bytes* to get to the next output row. Output pixel format -is set by NES_NTSC_OUT_DEPTH (defaults to 16-bit RGB). */ -void nes_ntsc_blit( nes_ntsc_t const* ntsc, unsigned char const* nes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ); - -/* Equivalent functions with color emphasis support. Source pixels are -9-bit values with the upper 3 bits specifying the emphasis bits from -PPU register 0x2001. */ -typedef struct nes_ntsc_emph_t nes_ntsc_emph_t; -void nes_ntsc_init_emph( nes_ntsc_emph_t* ntsc, nes_ntsc_setup_t const* setup ); -void nes_ntsc_blit_emph( nes_ntsc_emph_t const* ntsc, unsigned short const* nes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ); - -/* Number of output pixels written by blitter for given input width. Width might -be rounded down slightly; use NES_NTSC_IN_WIDTH() on result to find rounded -value. Guaranteed not to round 256 down at all. */ -#define NES_NTSC_OUT_WIDTH( in_width ) \ - (((in_width) - 1) / nes_ntsc_in_chunk * nes_ntsc_out_chunk + nes_ntsc_out_chunk) - -/* Number of input pixels that will fit within given output width. Might be -rounded down slightly; use NES_NTSC_OUT_WIDTH() on result to find rounded -value. */ -#define NES_NTSC_IN_WIDTH( out_width ) \ - ((out_width) / nes_ntsc_out_chunk * nes_ntsc_in_chunk - nes_ntsc_in_chunk + 1) - - -/* Interface for user-defined custom blitters. -Can be used with nes_ntsc_t and nes_ntsc_emph_t */ - -enum { nes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ -enum { nes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ -enum { nes_ntsc_black = 15 }; /* palette index for black */ -enum { nes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ - -/* Begin outputting row and start three pixels. First pixel will be cut off a bit. -Use nes_ntsc_black for unused pixels. Declares variables, so must be before first -statement in a block (unless you're using C++). */ -#define NES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ - char const* const ktable = \ - (char*) (ntsc)->table + burst * (nes_ntsc_burst_size * sizeof (ntsc_rgb_t));\ - NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, NES_NTSC_ENTRY_, ktable ) - -/* Begin input pixel */ -#define NES_NTSC_COLOR_IN( in_index, color_in ) \ - NTSC_COLOR_IN_( in_index, color_in, NES_NTSC_ENTRY_, ktable ) - -/* Generate output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: -24: RRRRRRRR GGGGGGGG BBBBBBBB -16: RRRRRGGG GGGBBBBB -15: RRRRRGG GGGBBBBB - 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ -#define NES_NTSC_RGB_OUT( index, rgb_out, bits ) \ - NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 ) - - -/* private */ -enum { nes_ntsc_entry_size = 128 }; -typedef unsigned long ntsc_rgb_t; -struct nes_ntsc_t { - ntsc_rgb_t table [nes_ntsc_palette_size * nes_ntsc_entry_size]; -}; -struct nes_ntsc_emph_t { - ntsc_rgb_t table [nes_ntsc_emph_palette_size * nes_ntsc_entry_size]; -}; -enum { nes_ntsc_burst_size = nes_ntsc_entry_size / nes_ntsc_burst_count }; - -#define NES_NTSC_ENTRY_( ktable, n ) \ - (ntsc_rgb_t*) (ktable + (n) * (nes_ntsc_entry_size * sizeof (ntsc_rgb_t))) - -/* deprecated */ -#define NES_NTSC_RGB24_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 24 ) -#define NES_NTSC_RGB16_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 16 ) -#define NES_NTSC_RGB15_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 15 ) -#define NES_NTSC_RAW_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 0 ) - -enum { nes_ntsc_min_in_width = 256 }; -enum { nes_ntsc_min_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_min_in_width ) }; - -enum { nes_ntsc_640_in_width = 271 }; -enum { nes_ntsc_640_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_640_in_width ) }; -enum { nes_ntsc_640_overscan_left = 8 }; -enum { nes_ntsc_640_overscan_right = nes_ntsc_640_in_width - 256 - nes_ntsc_640_overscan_left }; - -enum { nes_ntsc_full_in_width = 283 }; -enum { nes_ntsc_full_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_full_in_width ) }; -enum { nes_ntsc_full_overscan_left = 16 }; -enum { nes_ntsc_full_overscan_right = nes_ntsc_full_in_width - 256 - nes_ntsc_full_overscan_left }; - -/* common 3->7 ntsc macros */ -#define NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ - unsigned const ntsc_pixel0_ = (pixel0);\ - ntsc_rgb_t const* kernel0 = ENTRY( table, ntsc_pixel0_ );\ - unsigned const ntsc_pixel1_ = (pixel1);\ - ntsc_rgb_t const* kernel1 = ENTRY( table, ntsc_pixel1_ );\ - unsigned const ntsc_pixel2_ = (pixel2);\ - ntsc_rgb_t const* kernel2 = ENTRY( table, ntsc_pixel2_ );\ - ntsc_rgb_t const* kernelx0;\ - ntsc_rgb_t const* kernelx1 = kernel0;\ - ntsc_rgb_t const* kernelx2 = kernel0 - -#define NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ - ntsc_rgb_t raw_ =\ - kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ - kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ - NTSC_CLAMP_( raw_, shift );\ - NTSC_RGB_OUT_( rgb_out, bits, shift );\ -} - -/* common ntsc macros */ -#define ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) -#define ntsc_clamp_mask (ntsc_rgb_builder * 3 / 2) -#define ntsc_clamp_add (ntsc_rgb_builder * 0x101) -#define NTSC_CLAMP_( io, shift ) {\ - ntsc_rgb_t sub = (io) >> (9-(shift)) & ntsc_clamp_mask;\ - ntsc_rgb_t clamp = ntsc_clamp_add - sub;\ - io |= clamp;\ - clamp -= sub;\ - io &= clamp;\ -} - -#define NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ - unsigned color_;\ - kernelx##index = kernel##index;\ - kernel##index = (color_ = (color), ENTRY( table, color_ ));\ -} - -/* x is always zero except in snes_ntsc library */ -#define NTSC_RGB_OUT_( rgb_out, bits, x ) {\ - if ( bits == 16 )\ - rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ - if ( bits == 24 || bits == 32 )\ - rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ - if ( bits == 15 )\ - rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ - if ( bits == 14 )\ - rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ - if ( bits == 0 )\ - rgb_out = raw_ << x;\ -} - -#ifdef __cplusplus - } -#endif - -#endif - diff --git a/quicknes/nes_emu/nes_ntsc_impl.h b/quicknes/nes_emu/nes_ntsc_impl.h deleted file mode 100644 index 4d54c46b4e..0000000000 --- a/quicknes/nes_emu/nes_ntsc_impl.h +++ /dev/null @@ -1,520 +0,0 @@ - -/* Common implementation of NTSC filters */ - -#include -#include - -/* Copyright (C) 2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#define DISABLE_CORRECTION 0 - -#ifndef gamma_size - #if NTSC_STANDARD_INIT - #define gamma_size 256 - #else - #define gamma_size 1 - #endif -#endif - -#ifndef rgb_bits - #define rgb_bits 8 -#endif - -#ifndef LUMA_CUTOFF - #define LUMA_CUTOFF 0.20 -#endif - -#ifndef artifacts_max - #define artifacts_max (artifacts_mid * 1.5f) -#endif - -#ifndef fringing_max - #define fringing_max (fringing_mid * 2) -#endif - -#ifndef burst_count - #define burst_count 1 -#endif - -#ifndef rescale_in - #define rescale_in 1 - #define rescale_out 1 -#endif - -#ifndef std_decoder_hue - #define std_decoder_hue 0 -#endif - -#define ext_decoder_hue (std_decoder_hue + 15) - -#define NTSC_NAME2_( p, n ) p##_ntsc_##n -#define NTSC_NAME_( p, n ) NTSC_NAME2_( p, n ) -#define NTSC_NAME( name ) NTSC_NAME_( ntsc_prefix, name ) - -#define rgb_unit (1 << rgb_bits) -#define burst_size (NTSC_NAME( entry_size ) / burst_count) -#define ntsc_pixels NTSC_NAME( pixels ) - -#define rgb_offset (rgb_unit * 2 + 0.5f) - -#define NTSC_CLAMP( io ) \ - NTSC_CLAMP_( io, (8 - rgb_bits) ) - -enum { kernel_half = 16 }; -enum { kernel_size = kernel_half * 2 + 1 }; - -typedef struct ntsc_impl_t -{ - float to_float [gamma_size]; - float to_rgb [burst_count * 6]; - float contrast; - float brightness; - float artifacts; - float fringing; - float hue_warping; - float kernel [rescale_out * kernel_size * 2]; -} ntsc_impl_t; - -#undef PI -#define PI 3.14159265358979323846f - -#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ - float t;\ - t = i * cos_b - q * sin_b;\ - q = i * sin_b + q * cos_b;\ - i = t;\ -} - -static void init_ntsc_filters( ntsc_impl_t* impl, NTSC_NAME( setup_t ) const* setup ) -{ -#if rescale_out > 1 - float kernels [kernel_size * 2]; -#else - float* const kernels = impl->kernel; -#endif - - /* generate luma (y) filter using sinc kernel */ - { - /* sinc with rolloff (dsf) */ - float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; - float const maxh = 32; - float const pow_a_n = (float) pow( rolloff, maxh ); - float sum; - int i; - /* quadratic mapping to reduce negative (blurring) range */ - float to_angle = (float) setup->resolution + 1; - to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); - - kernels [kernel_size * 3 / 2] = maxh; /* default center value */ - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = i - kernel_half; - float angle = x * to_angle; - /* instability occurs at center point with rolloff very close to 1.0 */ - if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) - { - float rolloff_cos_a = rolloff * (float) cos( angle ); - float num = 1 - rolloff_cos_a - - pow_a_n * (float) cos( maxh * angle ) + - pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); - float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - float dsf = num / den; - kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; - } - } - - /* apply blackman window and find sum */ - sum = 0; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - float x = PI * 2 / (kernel_half * 2) * i; - float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); - sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); - } - - /* normalize kernel */ - sum = 1.0f / sum; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = kernel_size * 3 / 2 - kernel_half + i; - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - - /* generate chroma (iq) filter using gaussian kernel */ - { - float const cutoff_factor = -0.03125f; - float cutoff = (float) setup->bleed; - int i; - - if ( cutoff < 0 ) - { - /* keep extreme value accessible only near upper end of scale (1.0) */ - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= -30.0f / 0.65f; - } - cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; - - for ( i = -kernel_half; i <= kernel_half; i++ ) - kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); - - /* normalize even and odd phases separately */ - for ( i = 0; i < 2; i++ ) - { - float sum = 0; - int x; - for ( x = i; x < kernel_size; x += 2 ) - sum += kernels [x]; - - sum = 1.0f / sum; - for ( x = i; x < kernel_size; x += 2 ) - { - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - } - - /* - printf( "luma:\n" ); - for ( i = kernel_size; i < kernel_size * 2; i++ ) - printf( "%f\n", kernels [i] ); - printf( "chroma:\n" ); - for ( i = 0; i < kernel_size; i++ ) - printf( "%f\n", kernels [i] ); - */ - - /* generate linear rescale kernels */ - #if rescale_out > 1 - { - float weight = 1.0f; - float* out = impl->kernel; - do - { - float remain = 0; - int i; - weight -= 1.0f / rescale_in; - for ( i = 0; i < kernel_size * 2; i++ ) - { - float cur = kernels [i]; - float m = cur * weight; - *out++ = m + remain; - remain = cur - m; - } - } - while ( out < &impl->kernel [rescale_out * kernel_size * 2] ); - } - #endif -} - -static float const default_decoder [6] = - { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; - -static void init_ntsc_impl( ntsc_impl_t* impl, NTSC_NAME( setup_t ) const* setup ) -{ - impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; - impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; - - impl->artifacts = (float) setup->artifacts; - if ( impl->artifacts > 0 ) - impl->artifacts *= artifacts_max - artifacts_mid; - impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; - - impl->fringing = (float) setup->fringing; - if ( impl->fringing > 0 ) - impl->fringing *= fringing_max - fringing_mid; - impl->fringing = impl->fringing * fringing_mid + fringing_mid; - - init_ntsc_filters( impl, setup ); - - /* generate gamma table */ - if ( gamma_size > 1 ) - { - float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); - float const gamma = 1.1333f - (float) setup->gamma * 0.5f; - /* match common PC's 2.2 gamma to TV's 2.65 gamma */ - int i; - for ( i = 0; i < gamma_size; i++ ) - impl->to_float [i] = - (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; - } - - /* setup decoder matricies */ - { - float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; - float sat = (float) setup->saturation + 1; - float const* decoder = setup->decoder_matrix; - if ( !decoder ) - { - decoder = default_decoder; - hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); - } - - { - float s = (float) sin( hue ) * sat; - float c = (float) cos( hue ) * sat; - float* out = impl->to_rgb; - int n; - - n = burst_count; - do - { - float const* in = decoder; - int n = 3; - do - { - float i = *in++; - float q = *in++; - *out++ = i * c - q * s; - *out++ = i * s + q * c; - } - while ( --n ); - if ( burst_count <= 1 ) - break; - ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ - } - while ( --n ); - } - } -} - -/* kernel generation */ - -#define RGB_TO_YIQ( r, g, b, y, i ) (\ - (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ - (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ - ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ -) - -#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ - r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ - g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ - (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ -) - -#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) - -enum { rgb_kernel_size = burst_size / alignment_count }; -enum { ntsc_rgb_bias = rgb_unit * 2 * ntsc_rgb_builder }; - -typedef struct pixel_info_t -{ - int offset; - float negate; - float kernel [4]; -} pixel_info_t; - -#if rescale_in > 1 - #define PIXEL_OFFSET_( ntsc, scaled ) \ - (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ - (kernel_size * 2 * scaled)) - - #define PIXEL_OFFSET( ntsc, scaled ) \ - PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ - (((scaled) + rescale_out * 10) % rescale_out) ),\ - (1.0f - (((ntsc) + 100) & 2)) -#else - #define PIXEL_OFFSET( ntsc, scaled ) \ - (kernel_size / 2 + (ntsc) - (scaled)),\ - (1.0f - (((ntsc) + 100) & 2)) -#endif - -extern pixel_info_t const ntsc_pixels [alignment_count]; - -/* Generate pixel at all burst phases and column alignments */ -static void gen_kernel( ntsc_impl_t* impl, float y, float i, float q, ntsc_rgb_t* out ) -{ - /* generate for each scanline burst phase */ - float const* to_rgb = impl->to_rgb; - y -= rgb_offset; - do - { - /* Encode yiq into *two* composite signals (to allow control over artifacting). - Convolve these with kernels which: filter respective components, apply - sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack - into integer. Based on algorithm by NewRisingSun. */ - pixel_info_t const* pixel = ntsc_pixels; - do - { - /* negate is -1 when composite starts at odd multiple of 2 */ - float const yy = y * impl->fringing * pixel->negate; - float const ic0 = (i + yy) * pixel->kernel [0]; - float const qc1 = (q + yy) * pixel->kernel [1]; - float const ic2 = (i - yy) * pixel->kernel [2]; - float const qc3 = (q - yy) * pixel->kernel [3]; - - float const factor = impl->artifacts * pixel->negate; - float const ii = i * factor; - float const yc0 = (y + ii) * pixel->kernel [0]; - float const yc2 = (y - ii) * pixel->kernel [2]; - - float const qq = q * factor; - float const yc1 = (y + qq) * pixel->kernel [1]; - float const yc3 = (y - qq) * pixel->kernel [3]; - - float const* k = &impl->kernel [pixel->offset]; - int n; - for ( n = rgb_kernel_size; n; --n ) - { - float i = k[0]*ic0 + k[2]*ic2; - float q = k[1]*qc1 + k[3]*qc3; - float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + - k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; - if ( rescale_out <= 1 ) - k--; - else if ( k >= &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) - k -= kernel_size * 2 * (rescale_out - 1) + 2; - else - k += kernel_size * 2 - 1; - { - int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); - *out++ = PACK_RGB( r, g, b ) - ntsc_rgb_bias; - } - } - } - while ( pixel++ < &ntsc_pixels [alignment_count - 1] ); - - if ( burst_count <= 1 ) - break; - - to_rgb += 6; - - ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ - } - while ( to_rgb < &impl->to_rgb [burst_count * 6] ); -} - -/* only used by NES/SNES filters */ -static void merge_kernel_fields( ntsc_rgb_t* io ) -{ - if ( burst_count == 3 ) - { - int n; - for ( n = burst_size; n; --n ) - { - ntsc_rgb_t p0 = io [burst_size * 0] + ntsc_rgb_bias; - ntsc_rgb_t p1 = io [burst_size * 1] + ntsc_rgb_bias; - ntsc_rgb_t p2 = io [burst_size * 2] + ntsc_rgb_bias; - /* merge colors without losing precision */ - io [burst_size * 0] = - ((p0 + p1 - ((p0 ^ p1) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias; - io [burst_size * 1] = - ((p1 + p2 - ((p1 ^ p2) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias; - io [burst_size * 2] = - ((p2 + p0 - ((p2 ^ p0) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias; - ++io; - } - } -} - -#if DISABLE_CORRECTION - #define CORRECT_ERROR( a ) { out [i] += ntsc_rgb_bias; } - #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += ntsc_rgb_bias; } -#else - #define CORRECT_ERROR( a ) { out [a] += error; } - #define DISTRIBUTE_ERROR( a, b, c ) {\ - ntsc_rgb_t fourth = (error + 2 * ntsc_rgb_builder) >> 2;\ - fourth &= (ntsc_rgb_bias >> 1) - ntsc_rgb_builder;\ - fourth -= ntsc_rgb_bias >> 2;\ - out [a] += fourth;\ - out [b] += fourth;\ - out [c] += fourth;\ - out [i] += error - (fourth * 3);\ - } -#endif - -static void correct_errors( ntsc_rgb_t color, ntsc_rgb_t* out ); - -/* only used by palette-based filters (TI, VIC2) */ -#if NTSC_STANDARD_INIT -void NTSC_NAME( init )( NTSC_NAME( t )* ntsc, NTSC_NAME( setup_t ) const* setup ) -{ - ntsc_impl_t impl; - if ( !setup ) - setup = &NTSC_NAME( composite ); - init_ntsc_impl( &impl, setup ); - - { - int n = NTSC_NAME( palette_size ); - ntsc_rgb_t* kernel_out = (ntsc ? ntsc->table [0] : 0); - unsigned char* palette_out = setup->palette_out; - unsigned char const* palette = setup->palette; - if ( !palette ) - palette = default_palette [0]; - - do - { - float r = impl.to_float [*palette++]; - float g = impl.to_float [*palette++]; - float b = impl.to_float [*palette++]; - - float y, i, q = RGB_TO_YIQ( r, g, b, y, i ); - - { - int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); - ntsc_rgb_t rgb = PACK_RGB( r, g, b ); - - if ( palette_out ) - { - ntsc_rgb_t clamped = rgb; - NTSC_CLAMP( clamped ); - *palette_out++ = (unsigned char) (clamped >> 21); - *palette_out++ = (unsigned char) (clamped >> 11); - *palette_out++ = (unsigned char) (clamped >> 1); - } - - if ( kernel_out ) - { - gen_kernel( &impl, y, i, q, kernel_out ); - #if burst_count > 1 - if ( setup->merge_fields ) - merge_fields( kernel_out ); - #endif - correct_errors( rgb, kernel_out ); - kernel_out += burst_size; - } - } - } - while ( --n ); - } -} -#endif - -/* blitter related */ - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#ifndef restrict - #define restrict -#endif - -#include - -#if UINT_MAX == 0xFFFFFFFF - typedef unsigned int ntsc_uint32_t; -#elif ULONG_MAX == 0xFFFFFFFF - typedef unsigned long ntsc_uint32_t; -#else - #error "Need 32-bit int type" -#endif - -#if USHRT_MAX == 0xFFFF - typedef unsigned short ntsc_uint16_t; -#else - #error "Need 16-bit int type" -#endif - diff --git a/quicknes/nes_emu/nes_util.cpp b/quicknes/nes_emu/nes_util.cpp deleted file mode 100644 index b6eeb5f548..0000000000 --- a/quicknes/nes_emu/nes_util.cpp +++ /dev/null @@ -1,211 +0,0 @@ - -// Nes_Emu 0.7.0. http://www.slack.net/~ant/ - -#include "nes_util.h" - -#include "Nes_Cart.h" -#include "Nes_Emu.h" -#include -#include - -/* Copyright (C) 2004-2006 Shay Green. This module 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 -module is distributed in the hope that 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 module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "blargg_source.h" - -// Joypad_Filter - -Joypad_Filter::Joypad_Filter() -{ - prev = 0; - mask = ~0x50; - times [0] = 0; - times [1] = 0; - set_a_rate( 0.75 ); - set_b_rate( 0.75 ); -} - -void Joypad_Filter::enable_filtering( bool b ) -{ - bool enabled = (mask + 0x10) >> 5 & 1; - if ( enabled != b ) - mask = b ? ~0x50 : ~0; -} - -int Joypad_Filter::process( int joypad ) -{ - // prevent left+right and up+down (prefer most recent one pressed) - int changed = prev ^ joypad; - int hidden = joypad & ~mask; - prev = joypad; - - int const x_axis = 0xC0; - int const y_axis = 0x30; - - if ( changed & x_axis && hidden & x_axis ) - mask ^= x_axis; - - if ( changed & y_axis && hidden & y_axis ) - mask ^= y_axis; - - // reset turbo if button just pressed, to avoid delaying button press - if ( changed & 0x100 ) times [0] = 0; - if ( changed & 0x200 ) times [1] = 0; - mask |= changed & 0x300 & joypad; - - // mask and combine turbo bits - joypad &= mask; - return (joypad >> 8 & 3) | (joypad & ~0x300); -} - -void Joypad_Filter::clock_turbo() -{ - for ( int i = 0; i < 2; i++ ) - { - int t = times [i] + rates [i]; - mask ^= (t & 0x100) << i; - times [i] = t & 0xFF; - } -} - -// game_genie_patch_t - -blargg_err_t game_genie_patch_t::decode( const char* in ) -{ - int const code_len = 8; - unsigned char result [code_len] = { 0 }; - int in_len = strlen( in ); - if ( in_len != 6 && in_len != 8 ) - return "Game Genie code is wrong length"; - for ( int i = 0; i < code_len; i++ ) - { - char c = 'A'; - if ( i < in_len ) - c = toupper( in [i] ); - - static char const letters [17] = "AEPOZXLUGKISTVYN"; - char const* p = strchr( (char*) letters, c ); - if ( !p ) - return "Game Genie code had invalid character"; - int n = p - letters; - - result [i] |= n >> 1; - result [(i + 1) % code_len] |= (n << 3) & 0x0f; - } - - addr = result [3]<<12 | result [5]<<8 | result [2]<<4 | result [4]; - change_to = result [1]<<4 | result [0]; - compare_with = -1; - if ( addr & 0x8000 ) - compare_with = result [7]<<4 | result [6]; - addr |= 0x8000; - - return 0; -} - -int game_genie_patch_t::apply( Nes_Cart& cart ) const -{ - // determine bank size - long bank_size = 32 * 1024L; // mappers 0, 2, 3, 7, 11, 34, 71, 87 - switch ( cart.mapper_code() ) - { - case 1: // MMC1 - case 71: // Camerica - case 232: // Quattro - bank_size = 16 * 1024L; - break; - - case 4: // MMC3 - case 5: // MMC5 - case 24: // VRC6 - case 26: // VRC6 - case 69: // FME7 - bank_size = 8 * 1024L; - break; - } - - // patch each bank (not very good, since it might patch banks that never occupy - // that address) - int mask = (compare_with >= 0 ? ~0 : 0); - BOOST::uint8_t* p = cart.prg() + addr % bank_size; - int count = 0; - for ( int n = cart.prg_size() / bank_size; n--; p += bank_size ) - { - if ( !((*p ^ compare_with) & mask) ) - { - *p = change_to; - count++; - } - } - return count; -} - -// Cheat_Value_Finder - -Cheat_Value_Finder::Cheat_Value_Finder() -{ - emu = NULL; -} - -void Cheat_Value_Finder::start( Nes_Emu* new_emu ) -{ - emu = new_emu; - pos = 0; - memcpy( original, emu->low_mem(), low_mem_size ); - memset( changed, 0, low_mem_size ); -} - -void Cheat_Value_Finder::rescan() -{ - byte const* low_mem = emu->low_mem(); - for ( int i = 0; i < low_mem_size; i++ ) - changed [i] |= original [i] ^ low_mem [i]; - memcpy( original, emu->low_mem(), low_mem_size ); -} - -void Cheat_Value_Finder::search( int new_original, int new_changed ) -{ - require( new_original != new_changed ); - original_value = new_original; - changed_value = new_changed; - pos = -1; -} - -int Cheat_Value_Finder::next_match( int* addr ) -{ - byte const* low_mem = emu->low_mem(); - while ( ++pos < low_mem_size ) - { - if ( !changed [pos] ) - { - int old = (original [pos] - original_value) & 0xff; - int cur = (low_mem [pos] - changed_value) & 0xff; - - if ( old == cur ) - { - if ( addr ) - *addr = pos; - return (char) old; // sign-extend - } - } - } - - return no_match; -} - -int Cheat_Value_Finder::change_value( int new_value ) -{ - require( (unsigned) pos < low_mem_size ); - int result = emu->low_mem() [pos]; - emu->low_mem() [pos] = new_value; - return result; -} - diff --git a/quicknes/nes_emu/nes_util.h b/quicknes/nes_emu/nes_util.h deleted file mode 100644 index 809a12f60a..0000000000 --- a/quicknes/nes_emu/nes_util.h +++ /dev/null @@ -1,90 +0,0 @@ - -// Experimental utilities for NES emulator - -// Nes_Emu 0.7.0 - -#ifndef NES_UTIL_H -#define NES_UTIL_H - -#include "blargg_common.h" -class Nes_Emu; -class Nes_Cart; - -class Joypad_Filter { -public: - Joypad_Filter(); - - // Control filtering of simultaneous directions. Enabled by default. - void enable_filtering( bool = true ); - - // Prevents simultaneous left+right and up+down to avoid problems in some games. - // Also turns bits 8 and 9 into turbo A and B. - int process( int joypad ); - - // Set A and B turbo rates, where 1.0 is maximum and 0.0 disables them - void set_a_rate( double r ) { rates [0] = (int) (r * 0x100); } - void set_b_rate( double r ) { rates [1] = (int) (r * 0x100); } - - // Call after each emulated frame for which Nes_Emu::frame().joypad_read_count - // is non-zero. - void clock_turbo(); - -private: - int prev; - int mask; - int times [2]; - int rates [2]; -}; - -struct game_genie_patch_t -{ - unsigned addr; // always 0x8000 or greater - int change_to; - int compare_with; // if -1, always change byte - - // Decode Game Genie code - blargg_err_t decode( const char* in ); - - // Apply patch to cartridge data. Might not work for some codes, since this really - // requires emulator support. Returns number of bytes changed, where 0 - // means patch wasn't for that cartridge. - int apply( Nes_Cart& ) const; -}; - -class Cheat_Value_Finder { -public: - Cheat_Value_Finder(); - - // Start scanning emulator's memory for values that are constantly changing. - void start( Nes_Emu* ); - - // Rescan memory and eliminate any changed bytes from later matching. - // Should be called many times after begin_scan() and before begin_matching(). - void rescan(); - - // Start search for any bytes which changed by difference between original and - // changed values. - void search( int original, int changed ); - - // Get next match and return its delta from changed value (closer to 0 - // is more likely to be a match), or no_match if there are no more matches. - // Optionally returns address of matched byte. - enum { no_match = 0x100 }; - int next_match( int* addr = NULL ); - - // Change current match to new value. Returns previous value. - int change_value( int new_value ); - -private: - typedef BOOST::uint8_t byte; - Nes_Emu* emu; - int original_value; - int changed_value; - int pos; - enum { low_mem_size = 0x800 }; - byte original [low_mem_size]; - byte changed [low_mem_size]; -}; - -#endif - diff --git a/src/BizHawk.Client.EmuHawk/MainForm.Events.cs b/src/BizHawk.Client.EmuHawk/MainForm.Events.cs index 397fa53bf1..e2fd81db5d 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1425,7 +1425,7 @@ namespace BizHawk.Client.EmuHawk => GenericCoreConfig.DoDialogFor( this, settable, - "QuickNES Controller Settings", + CoreNames.QuickNes + " Controller Settings", isMovieActive: MovieSession.Movie.IsActive(), ignoreSettings: true); diff --git a/src/BizHawk.Client.EmuHawk/images/QuickNes.ico b/src/BizHawk.Client.EmuHawk/images/QuickNes.ico index df4bcb179ccd2be3fb1bc6ce49a077f2a3fc4d1d..488ab2a91b49d45fb82273f924607ae0fa79e080 100644 GIT binary patch literal 1150 zcmc(dy-LJD5XVOll-(?(vak>f5kF=(9@yB3b{0N>;2jn=zJMScR#v{pVdc9B*ZBx` zc9!Gp*JKluLK_n{lbxC0e=?f^SmC>_fuGxOw+gTU0DHXH#h&PA17{rj>qV8DQ)MYG zH~)=Jm-QOMJZmr78le{N%eup?a2h{*{8x8BS~vBDOEUJv_eN^6*pGAijKN+CC5R`tUvFE{55nf7n^;Ye*emvtKv(@KSPAGa2PI&Lpow@>=10m=V9W z*Q0#y)7!)bzOtt+?=F7u{eM=lk#|?ZuODnBP0Bi?@HNKqE&EE#v6mEIMmC>koCPw? zuIOub(`B1;iJ9UYi(Z4K#wk_!A-P37_>8;Hy6rr7XOZfox{Za|_cQs#y8D^U)@$bX G|F++;LL$ll literal 9062 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 - /// setup extra mappers. should be done before anything else - /// - [BizImport(CallingConvention.Cdecl)] - public abstract void qn_setup_mappers(); ///

/// create a new quicknes context /// @@ -48,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES /// pad 2 input /// string error [BizImport(CallingConvention.Cdecl)] - public abstract IntPtr qn_emulate_frame(IntPtr e, int pad1, int pad2); + public abstract IntPtr qn_emulate_frame(IntPtr e, uint pad1, uint pad2); /// /// blit to rgb32 /// @@ -239,11 +234,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES || s == " truncated file" // This is a garbage rom not worth anyone's time but at least NesHawk handles these better, and these occur before the core has a chance to assess an unsupported mapper ) { - throw new Common.UnsupportedGameException("Quicknes unsupported mapper"); + throw new Common.UnsupportedGameException(CoreNames.QuickNes + " unsupported mapper"); } else { - throw new InvalidOperationException("LibQuickNES error: " + s); + throw new InvalidOperationException($"{nameof(LibQuickNES)} error: {s}"); } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs index 8a16ab337b..012c651beb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs @@ -151,18 +151,32 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES } } + public enum Port1PeripheralOption : byte + { + Unplugged = 0x0, + Gamepad = 0x1, + FourScore = 0x2, + //FourScore2 = 0x3, // not available for port 1 + } + + public enum Port2PeripheralOption : byte + { + Unplugged = 0x0, + Gamepad = 0x1, + //FourScore = 0x2, // not available for port 2 + FourScore2 = 0x3, + } + [CoreSettings] public class QuickNESSyncSettings { - [DefaultValue(true)] - [DisplayName("Left Port Connected")] - [Description("Specifies whether or not the Left (Player 1) Controller is connected")] - public bool LeftPortConnected { get; set; } + [DefaultValue(Port1PeripheralOption.Gamepad)] + [DisplayName("Left Port Peripheral")] + public Port1PeripheralOption Port1 { get; set; } = Port1PeripheralOption.Gamepad; - [DefaultValue(false)] - [DisplayName("Right Port Connected")] - [Description("Specifies whether or not the Right (Player 2) Controller is connected")] - public bool RightPortConnected { get; set; } + [DefaultValue(Port2PeripheralOption.Unplugged)] + [DisplayName("Right Port Peripheral")] + public Port2PeripheralOption Port2 { get; set; } = Port2PeripheralOption.Unplugged; public QuickNESSyncSettings() { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs index c75bd9348f..5afc7bfd3c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs @@ -12,7 +12,11 @@ using BizHawk.BizInvoke; namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES { - [PortedCore(CoreNames.QuickNes, "", "0.7.0", "https://github.com/kode54/QuickNES")] + [PortedCore( + name: CoreNames.QuickNes, + author: "SergioMartin86, kode54, Blargg", + portedVersion: "1.0.0", + portedUrl: "https://github.com/SergioMartin86/quickerNES")] [ServiceNotApplicable(new[] { typeof(IDriveLight) })] public sealed partial class QuickNES : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IInputPollable, IBoardInfo, IVideoLogicalOffsets, IStatable, IDebuggable, @@ -21,9 +25,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES static QuickNES() { var resolver = new DynamicLibraryImportResolver( - $"libquicknes{(OSTailoredCode.IsUnixHost ? ".dll.so.0.7.0" : ".dll")}", hasLimitedLifetime: false); + $"libquicknes{(OSTailoredCode.IsUnixHost ? ".so" : ".dll")}", hasLimitedLifetime: false); QN = BizInvoker.GetInvoker(resolver, CallingConventionAdapters.Native); - QN.qn_setup_mappers(); } [CoreConstructor(VSystemID.Raw.NES, Priority = CorePriority.Low)] @@ -48,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES int mapper = 0; string mappername = Marshal.PtrToStringAnsi(QN.qn_get_mapper(Context, ref mapper)); - Console.WriteLine("QuickNES: Booted with Mapper #{0} \"{1}\"", mapper, mappername); + Console.WriteLine($"{CoreNames.QuickNes}: Booted with Mapper #{mapper} \"{mappername}\""); BoardName = mappername; PutSettings(settings ?? new QuickNESSettings()); @@ -80,64 +83,137 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES private void SetControllerDefinition() { ControllerDefinition def = new("NES Controller"); - if (_syncSettings.LeftPortConnected || _syncSettings.RightPortConnected) - def.BoolButtons.AddRange(PadP1.Select(p => p.Name)); - if (_syncSettings.LeftPortConnected && _syncSettings.RightPortConnected) - def.BoolButtons.AddRange(PadP2.Select(p => p.Name)); + void AddButtons(IEnumerable<(string PrefixedName, uint Bitmask)> entries) + => def.BoolButtons.AddRange(entries.Select(static p => p.PrefixedName)); + AddButtons(_syncSettings.Port1 switch + { + Port1PeripheralOption.Gamepad => GamepadButtons[0], + Port1PeripheralOption.FourScore => FourScoreButtons[0], + _ => Enumerable.Empty<(string PrefixedName, uint Bitmask)>() + }); + AddButtons(_syncSettings.Port2 switch + { + Port2PeripheralOption.Gamepad => GamepadButtons[1], + Port2PeripheralOption.FourScore2 => FourScoreButtons[1], + _ => Enumerable.Empty<(string PrefixedName, uint Bitmask)>() + }); def.BoolButtons.AddRange(new[] { "Reset", "Power" }); // console buttons ControllerDefinition = def.MakeImmutable(); } - private struct PadEnt + private static readonly (string PrefixedName, uint Bitmask)[][] GamepadButtons = new[] { - public readonly string Name; - public readonly int Mask; - public PadEnt(string Name, int Mask) - { - this.Name = Name; - this.Mask = Mask; - } - } - - private static PadEnt[] GetPadList(int player) - { - string prefix = $"P{player} "; - return PadNames.Zip(PadMasks, (s, i) => new PadEnt(prefix + s, i)).ToArray(); - } - - private static readonly string[] PadNames = - { - "Up", "Down", "Left", "Right", "Start", "Select", "B", "A" - }; - private static readonly int[] PadMasks = - { - 16, 32, 64, 128, 8, 4, 2, 1 + new[] { + ("P1 Up", 0b0000_0000_0000_0000_0000_0000_0001_0000u), + ("P1 Down", 0b0000_0000_0000_0000_0000_0000_0010_0000u), + ("P1 Left", 0b0000_0000_0000_0000_0000_0000_0100_0000u), + ("P1 Right", 0b0000_0000_0000_0000_0000_0000_1000_0000u), + ("P1 Start", 0b0000_0000_0000_0000_0000_0000_0000_1000u), + ("P1 Select", 0b0000_0000_0000_0000_0000_0000_0000_0100u), + ("P1 B", 0b0000_0000_0000_0000_0000_0000_0000_0010u), + ("P1 A", 0b0000_0000_0000_0000_0000_0000_0000_0001u), + }, + new[] { + ("P2 Up", 0b0000_0000_0000_0000_0000_0000_0001_0000u), + ("P2 Down", 0b0000_0000_0000_0000_0000_0000_0010_0000u), + ("P2 Left", 0b0000_0000_0000_0000_0000_0000_0100_0000u), + ("P2 Right", 0b0000_0000_0000_0000_0000_0000_1000_0000u), + ("P2 Start", 0b0000_0000_0000_0000_0000_0000_0000_1000u), + ("P2 Select", 0b0000_0000_0000_0000_0000_0000_0000_0100u), + ("P2 B", 0b0000_0000_0000_0000_0000_0000_0000_0010u), + ("P2 A", 0b0000_0000_0000_0000_0000_0000_0000_0001u), + }, }; - private static readonly PadEnt[] PadP1 = GetPadList(1); - private static readonly PadEnt[] PadP2 = GetPadList(2); - - private int GetPad(IController controller, IEnumerable buttons) + private static readonly (string PrefixedName, uint Bitmask)[][] FourScoreButtons = new[] { - int ret = 0; - foreach (var b in buttons) + new[] { + ("P1 Up", 0b0000_0000_0000_0000_0000_0000_0001_0000u), + ("P1 Down", 0b0000_0000_0000_0000_0000_0000_0010_0000u), + ("P1 Left", 0b0000_0000_0000_0000_0000_0000_0100_0000u), + ("P1 Right", 0b0000_0000_0000_0000_0000_0000_1000_0000u), + ("P1 Start", 0b0000_0000_0000_0000_0000_0000_0000_1000u), + ("P1 Select", 0b0000_0000_0000_0000_0000_0000_0000_0100u), + ("P1 B", 0b0000_0000_0000_0000_0000_0000_0000_0010u), + ("P1 A", 0b0000_0000_0000_0000_0000_0000_0000_0001u), + + ("P3 Up", 0b0000_0000_0000_0000_0001_0000_0000_0000u), + ("P3 Down", 0b0000_0000_0000_0000_0010_0000_0000_0000u), + ("P3 Left", 0b0000_0000_0000_0000_0100_0000_0000_0000u), + ("P3 Right", 0b0000_0000_0000_0000_1000_0000_0000_0000u), + ("P3 Start", 0b0000_0000_0000_0000_0000_1000_0000_0000u), + ("P3 Select", 0b0000_0000_0000_0000_0000_0100_0000_0000u), + ("P3 B", 0b0000_0000_0000_0000_0000_0010_0000_0000u), + ("P3 A", 0b0000_0000_0000_0000_0000_0001_0000_0000u), + }, + new[] { + ("P2 Up", 0b0000_0000_0000_0000_0000_0000_0001_0000u), + ("P2 Down", 0b0000_0000_0000_0000_0000_0000_0010_0000u), + ("P2 Left", 0b0000_0000_0000_0000_0000_0000_0100_0000u), + ("P2 Right", 0b0000_0000_0000_0000_0000_0000_1000_0000u), + ("P2 Start", 0b0000_0000_0000_0000_0000_0000_0000_1000u), + ("P2 Select", 0b0000_0000_0000_0000_0000_0000_0000_0100u), + ("P2 B", 0b0000_0000_0000_0000_0000_0000_0000_0010u), + ("P2 A", 0b0000_0000_0000_0000_0000_0000_0000_0001u), + + ("P4 Up", 0b0000_0000_0000_0000_0001_0000_0000_0000u), + ("P4 Down", 0b0000_0000_0000_0000_0010_0000_0000_0000u), + ("P4 Left", 0b0000_0000_0000_0000_0100_0000_0000_0000u), + ("P4 Right", 0b0000_0000_0000_0000_1000_0000_0000_0000u), + ("P4 Start", 0b0000_0000_0000_0000_0000_1000_0000_0000u), + ("P4 Select", 0b0000_0000_0000_0000_0000_0100_0000_0000u), + ("P4 B", 0b0000_0000_0000_0000_0000_0010_0000_0000u), + ("P4 A", 0b0000_0000_0000_0000_0000_0001_0000_0000u), + }, + }; + + + + private void SetPads(IController controller, out uint j1, out uint j2) + { + static uint PackGamepadButtonsFor(int portNumber, IController controller) { - if (controller.IsPressed(b.Name)) - ret |= b.Mask; + uint ret = unchecked(0xFFFFFF00u); + foreach (var (prefixedName, bitmask) in GamepadButtons[portNumber]) + { + if (controller.IsPressed(prefixedName)) ret |= bitmask; + } + return ret; } - return ret; - } - private void SetPads(IController controller, out int j1, out int j2) - { - if (_syncSettings.LeftPortConnected) - j1 = GetPad(controller, PadP1) | unchecked((int)0xffffff00); - else - j1 = 0; - if (_syncSettings.RightPortConnected) - j2 = GetPad(controller, _syncSettings.LeftPortConnected ? PadP2 : PadP1) | unchecked((int)0xffffff00); - else - j2 = 0; + static uint PackFourscoreButtonsFor(int portNumber, IController controller) + { + uint ret = 0; + if (portNumber == 0) ret |= 0b1111_1111_0000_1000_0000_0000_0000_0000u; + if (portNumber == 1) ret |= 0b1111_1111_0000_0100_0000_0000_0000_0000u; + + foreach (var (prefixedName, bitmask) in FourScoreButtons[portNumber]) + { + if (controller.IsPressed(prefixedName)) ret |= bitmask; + } + return ret; + } + + j1 = 0; + j2 = 0; + switch (_syncSettings.Port1) + { + case Port1PeripheralOption.Gamepad: + j1 = PackGamepadButtonsFor(0, controller); + break; + case Port1PeripheralOption.FourScore: + j1 = PackFourscoreButtonsFor(0, controller); + break; + } + switch (_syncSettings.Port2) + { + case Port2PeripheralOption.Gamepad: + j2 = PackGamepadButtonsFor(1, controller); + break; + case Port2PeripheralOption.FourScore2: + j2 = PackFourscoreButtonsFor(1, controller); + break; + } } public bool FrameAdvance(IController controller, bool render, bool rendersound = true) diff --git a/src/BizHawk.Emulation.Cores/CoreNames.cs b/src/BizHawk.Emulation.Cores/CoreNames.cs index 2eefc0d761..34f729b723 100644 --- a/src/BizHawk.Emulation.Cores/CoreNames.cs +++ b/src/BizHawk.Emulation.Cores/CoreNames.cs @@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores public const string Octoshock = "Octoshock"; public const string PceHawk = "PCEHawk"; public const string PicoDrive = "PicoDrive"; - public const string QuickNes = "QuickNes"; + public const string QuickNes = "quickerNES"; public const string Sameboy = "SameBoy"; public const string Saturnus = "Saturnus"; public const string SMSHawk = "SMSHawk"; diff --git a/src/BizHawk.Emulation.Cores/vpads_schemata/NesSchema.cs b/src/BizHawk.Emulation.Cores/vpads_schemata/NesSchema.cs index 0673f65b8f..fc9fccb088 100644 --- a/src/BizHawk.Emulation.Cores/vpads_schemata/NesSchema.cs +++ b/src/BizHawk.Emulation.Cores/vpads_schemata/NesSchema.cs @@ -140,21 +140,23 @@ namespace BizHawk.Emulation.Cores // Quicknes Can support none, one or two controllers. { var ss = ((QuickNES)core).GetSyncSettings(); - if (ss.LeftPortConnected && ss.RightPortConnected) + var playerNo = 1; + switch (ss.Port1) { - // Set both controllers - yield return StandardController(1); - yield return StandardController(2); + case QuickNES.Port1PeripheralOption.Gamepad: + yield return StandardController(playerNo++); + break; + case QuickNES.Port1PeripheralOption.FourScore: + throw new NotImplementedException("TODO"); } - else if (ss.LeftPortConnected && !ss.RightPortConnected) + switch (ss.Port2) { - yield return StandardController(1); + case QuickNES.Port2PeripheralOption.Gamepad: + yield return StandardController(playerNo++); + break; + case QuickNES.Port2PeripheralOption.FourScore2: + throw new NotImplementedException("TODO"); } - else if (!ss.LeftPortConnected && ss.RightPortConnected) - { - yield return StandardController(1); - } - yield return NesConsoleButtons(); } }