35 Commits

Author SHA1 Message Date
C.W. Betts 2d8636904c Poke the plists: get the development language from Xcode build. 2020-10-01 01:51:37 -06:00
C.W. Betts f0b732c227 Fix locations of the system plugin headers.
Minor Xcode maintenance.
2020-10-01 01:26:41 -06:00
C.W. Betts 819d8ff77c Revert "set deployment target to match OpenEmuCore's, of 10.14.4." 2020-09-22 09:28:11 -06:00
C.W. Betts 925d78700b set deployment target to match OpenEmuCore's, of 10.14.4. 2020-09-22 02:46:35 -06:00
Stuart Carnie fa72a9f10b fix: Remove VALID_ARCH restriction to allow universal support 2020-07-06 17:43:40 -07:00
C.W. Betts 8474ecf2c3 Update language resources.
This quiets warnings in newer Xcode releases.

Also update framework locations.
2020-01-07 16:44:38 -07:00
Stuart Carnie bd88d61225 fix: crash 2019-06-02 12:18:49 -07:00
clobber 8bb34a7344 Stubs for future mouse paddle support 2018-11-25 16:03:03 -06:00
clobber ff0473d70d Bump version for sparkle updater. Core is still 3.9.3 2018-11-25 15:08:15 -06:00
clobber fcd9a2b709 Cleanup 2018-11-25 15:08:06 -06:00
clobber 06dade8f65 Add display mode change support 2018-11-11 19:54:23 -06:00
clobber af845d3c41 Fix PAR for PAL 2018-11-10 00:51:39 -06:00
clobber 946332d472 Cleanup 2018-11-10 00:46:57 -06:00
Rudy Richter bd8ce9e153 Enable direct-rendering 2017-07-22 10:39:51 -04:00
clobber 01f037446b Bump version for sparkle updater. Core is still 3.9.3 2016-07-01 12:21:12 -05:00
clobber cbec084d68 Limit rewind buffer to 60 seconds to reduce memory use 2016-06-30 01:36:55 -05:00
clobber 551c15eba5 Bump version in order for sparkle updater to work. Core is still 3.9.3 2015-12-22 20:03:54 -07:00
clobber b62c66577e Clean up deprecated methods. 2015-12-12 17:07:14 -08:00
Alexander Strange 03c673848e Update projects - fix debug builds, make deployment 10.11, enable objc-arc properly, build faster 2015-10-17 13:04:19 -07:00
clobber 1c002610fd Turn off GCC_NO_COMMON_BLOCKS 2015-10-09 19:41:43 -05:00
Christoph Leimbrock 8b1fd4728e Fix some warning and adjust project settings. 2015-10-06 22:04:32 +02:00
clobber 6de171184b Fix PAR 2015-07-21 03:37:02 -05:00
Kyle Lacy 0058de535e Fix memory leak 2015-06-10 15:26:22 -07:00
Kyle Lacy 44a3e1c7d3 Add plist keys to support rewinding 2015-03-04 18:58:38 -08:00
Kyle Lacy 63561f1d8d Add state serialization/deserialization 2015-03-04 18:58:00 -08:00
Kyle Lacy 16bd173e3b Make Stella's Serializer private members public
The public API for `Serializer` doesn't provide enough information to
just read out all bytes (since there's no way to get the stream's
length). This is the most minimal change that can be made to Stella's
internals to support state serialization
2015-03-04 18:57:28 -08:00
clobber 4c68b2fd96 Input fixup 2014-02-18 22:22:28 -06:00
clobber 9a53f0d247 Cleanup 2014-02-18 21:29:09 -06:00
clobber 7d65671023 Add console switches to input 2014-02-18 21:11:31 -06:00
clobber 4e2330e6c5 Update port to 3.9.3 2014-02-16 18:10:36 -06:00
clobber 594f5e604c Check-in the rest of 3.9.3 source for real this time 2014-02-16 13:27:32 -06:00
clobber a6829f3817 Check-in 3.9.3 source 2014-02-16 13:20:44 -06:00
clobber e4d3ae497d Add error: parameter to -loadFileAtPath: method. 2014-02-08 14:48:21 -08:00
Alexander Strange e07710ae7b Compilation speed: enable modules 2014-01-19 23:10:12 -08:00
clobber 762ddaf8cf Update spakle update URL and remove DSA key 2013-12-21 21:57:58 -06:00
611 changed files with 191415 additions and 12031 deletions
+99 -646
View File
@@ -1,647 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1060</int>
<string key="IBDocument.SystemVersion">11C74</string>
<string key="IBDocument.InterfaceBuilderVersion">1938</string>
<string key="IBDocument.AppKitVersion">1138.23</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">1938</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>NSTextField</string>
<string>NSCustomObject</string>
<string>NSMatrix</string>
<string>NSStepper</string>
<string>NSNumberFormatter</string>
<string>NSCustomView</string>
<string>NSButtonCell</string>
<string>NSStepperCell</string>
<string>NSTextFieldCell</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="NSCustomObject" id="1001">
<string key="NSClassName">GBAControlsPreference</string>
</object>
<object class="NSCustomObject" id="1003">
<string key="NSClassName">FirstResponder</string>
</object>
<object class="NSCustomObject" id="1004">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSCustomView" id="1005">
<reference key="NSNextResponder"/>
<int key="NSvFlags">268</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="NSTextField" id="529444683">
<reference key="NSNextResponder" ref="1005"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{168, 260}, {101, 17}}</string>
<reference key="NSSuperview" ref="1005"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="806786179"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="262381886">
<int key="NSCellFlags">68288064</int>
<int key="NSCellFlags2">272630784</int>
<string key="NSContents">Show bindings:</string>
<object class="NSFont" key="NSSupport" id="385894065">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">13</double>
<int key="NSfFlags">1044</int>
</object>
<reference key="NSControlView" ref="529444683"/>
<object class="NSColor" key="NSBackgroundColor" id="976045936">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
</object>
</object>
<object class="NSColor" key="NSTextColor" id="522026884">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlTextColor</string>
<object class="NSColor" key="NSColor" id="909323645">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
</object>
</object>
</object>
<object class="NSMatrix" id="806786179">
<reference key="NSNextResponder" ref="1005"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{274, 259}, {168, 18}}</string>
<reference key="NSSuperview" ref="1005"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<bool key="NSEnabled">YES</bool>
<int key="NSNumRows">1</int>
<int key="NSNumCols">2</int>
<array class="NSMutableArray" key="NSCells">
<object class="NSButtonCell" id="165264936">
<int key="NSCellFlags">-2080244224</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Keyboard</string>
<reference key="NSSupport" ref="385894065"/>
<reference key="NSControlView" ref="806786179"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">0</int>
<object class="NSButtonImageSource" key="NSAlternateImage" id="671804640">
<string key="NSImageName">NSRadioButton</string>
</object>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
<object class="NSButtonCell" id="223753203">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Gamepad</string>
<reference key="NSSupport" ref="385894065"/>
<reference key="NSControlView" ref="806786179"/>
<int key="NSTag">1</int>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">0</int>
<object class="NSImage" key="NSNormalImage">
<int key="NSImageFlags">549453824</int>
<string key="NSSize">{18, 18}</string>
<array class="NSMutableArray" key="NSReps">
<array>
<integer value="0"/>
<object class="NSBitmapImageRep">
<object class="NSData" key="NSTIFFRepresentation">
<bytes key="NS.bytes">TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw
IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/
29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5
dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA
AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG
AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/
0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/
7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/
5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/
3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD
AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns
AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/
6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/
/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/
///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl
YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA
AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD
AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu
AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB
AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES
AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS
AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAABEgAAAF1gAAAAAACAAIAAgACAABAAEAAQABAAARIGFw
cGwCAAAAbW50clJHQiBYWVogB9kABQANAAAAOwAMYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAPbWAAEAAAAA0y1hcHBsvQkfoRizoa3hZ+SMa9pbkwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA
AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAYSbmRpbgAA
B+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAJCbW1vZAAAENQAAAAoY3BydAAAEPwAAAAkWFlaIAAA
AAAAAGVxAAA6MQAACZZYWVogAAAAAAAAaloAAK9yAAAa+VhZWiAAAAAAAAAnCwAAFncAAK6WWFlaIAAA
AAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbGN1
cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAAD
AQAAAgAAACEAcgDsAYICNQMBA/0FJAZqB84JXwsFDNYOxBDCEtwU8hczGWQbpx3qICsiciSnJuEpDys6
LUcvNjEdMwc07jbSOLI6jDxZPiE/5kGeQ1JE/UadSDdJyktZTNROVU/CUTJSmVP7VVlWulgOWV9asFv0
XTRecl+mYN5iEWNGZHNlnWbCZ+ZpCmopa0ZsXm12bo1voHCvcb9y0HPhdPF2AHcOeB55MHo2exl77nzC
fZZ+cX9IgCKA/YHagruDnoSAhWmGU4c/iC+JIYoWiw2MBYz/jf2O/o//kQGSA5MGlAaU+5XilsaXq5iS
mXeaYptRnD6dLZ4enxCgBKD4oeyi4KPVpMeluqawp52ojal8qmyrV6xArSiuD67+sACxB7ILsw60DbUM
tgu3B7f/uPS56breu9C8wb2wvp+/isB3wWbCVcNExDXFJcYYxw3IAsj4yenKwMuIzEzNDs3PzpDPT9AO
0MnRhNI/0vnTr9Rm1R3V09aL10LX+Nit2WLaGNrP24bcPdz03bDeZd8M35ngJOCt4TnhxuJU4uPjdeQH
5JjlLOXA5lPm5ud56Azon+ky6cTqU+rj63TsBeyT7SHtr+477sXvUu/g8Grw8/F68gLyjPMW86D0KPSu
9TL1tfYv9qL3Dfdm9774C/hF+H74uPjx+WT53vrU/H3+i///AAAAJAB8AQIBkAJAAxQD/gUjBlwHuQk7
CtIMiw5jEEoSOhRCFloYfRqhHMce7iEeI0slZCeEKZQrcy0kLsgwcTIaM8I1aTcOOKo6QjvaPW0+/ECI
Qg1Di0UFRnxH50lWSrVMFE1uTsNQFlFqUrJT91U9VnZXr1jjWhNbRFxtXZtew1/mYQZiJGNDZF9leGaO
Z6VovGnPauFr820Ibh5vMnBKcVxydnOQdKF1iHZldzx4F3j1edN6sXuRfHB9Vn46fx6ABIDsgdaCwIOr
hJaFgoZuh1qISIk3iiWLEov/jOuN1Y6/j6OQh5FsklKTN5QflQqV85bel8qYtpmkmpGbf5xsnVmeRZ8y
oB+hCqH0ouCjzaS0pZumhKdoqFqpZqp7q4ismq2krq2vvbDFscyyzrPRtNa12LbZt9m427nYutm73rzh
vea+77/1wQDCDcMbxCrFL8YdxvnHz8ikyXrKUcspzADM1s2szoPPWtAv0QPR19Kr03/UU9Ul1fTWv9eN
2FrZJtnw2rjbgtxI3Qfdtt5k3xHfwOBv4R/hzeJ64ynj1+SD5S7l1uZ95yTnyehu6RHpsOpR6vHrjuwq
7MbtYe377pHvKO/C8Fjw7PF/8hLyqPM+89L0YvTx9YH2EPae9yT3nPgY+KD5MfnU+pH7Z/xj/YT+m/8g
/1j/j//H//8AAAAVAEoAmQEBAXEB9QKOAzwEAATbBdMG2wgACSwKdwvHDSUOig/6EXES5RRZFcsXOBik
GgobZxy+HgsfUCCYId8jJyRqJakm4ygbKU8qfiurLNIt9i8WMDIxSzJWM2c0cDV3Nnw3fDh6OXk6cjto
PGU9ZD5mP2ZAX0FaQlJDTERBRTNGIUcQSABI7knZSsNLrkyYTYBOZk9PUDhRJFIOUvlT5FTQVb5WpFd6
WEpZFVniWrFbgFxPXRxd6l66X4lgVmElYfRiwmORZGBlLGX5ZsZnkWhfaSxp+GrDa41sVm0bbdVuhm8w
b9lwgnEmcc1ydXMac790ZXUJda92VXb6d6F4SXjyeZt6SHr1e6N8U30Gfbp+bX8kf9uAlYFTgg+CzIOK
hESFAIW+hnuHN4fziK+JbYoqiueLpIxijR6N246bj1qQG5DbkZuSXpMhk+SUqJVrljSXApfRmJ+Zb5pA
mxOb5Zy3nYueYp85oBGg6qHFoqGjgqRkpUWmJ6cJp+2o06m6qqGriKx0rV6uSa83sCOxD7IAsvKz47TZ
tc22w7e3uLS5q7qlu6C8m72Yvpe/k8CNwYrCh8OGxIDFecZ1x3DIZslmynrLk8ynzbbOus/B0MHRwNLJ
09XU3dXz1xrYStmD2sjcM92u30rhBeLm5PrnYuoP7S/w5/WS+6X//wAAbmRpbgAAAAAAAAY2AACVRgAA
Vs8AAFKpAACRTgAAKBAAABVgAABQDQAAVDkAAtwoAAKo9QABczMAAwEAAAIAAAAYADIATQBmAH4AlgCu
AMYA3QD0AQwBIgE6AVABaAF/AZcBrgHGAd8B+AIQAigCQgJcAnYCkAKrAsYC4gL/AxsDOANVA3QDkwOz
A9MD8wQVBDcEWgR9BKMEyQTyBRwFRwVzBZ8FzAX5BigGVwaHBrgG6QccB1AHhge9B/QILAhmCKII3gkc
CVsJnAnfCiMKaQqwCvkLQwuSC+EMMAyFDNoNMA2JDeQOQQ6gDv4PYA/GEC0QlBEAEXAR4RJUEs0TRBO/
FDwUuRU7Fb8WRxbSF10X6xh9GREZqRpDGt4bfRwhHMUdbB4SHrwfZyAVIMUhdCIlIuIjxiSyJaEmiid5
KGcpVypEKzAsIC0LLfYu4S/KMLMxnDKDM200VTU7NiI3Cjf0ON45yzq+O8c82z3uPwFAGUEpQjlDTURf
RXJGhUeYSK5JxUrdS/tNGU41T15QhVGwUtpUEVVIVoVXxFjmWf9bIFxBXWhelV/AYPBiJmNkZKRl5Wcr
aHZpxWsWbG9txW8acHRxy3MmdIB113cweIl543tyfSN+4oClgm6EO4YVh/WJ1YvGjbiPr5Gmk6GVp5et
mbGbuJ3Dn8Oh36SSp1uqGKzTr4CyNLTft5m6VL0Uv9/CvMWQyH7LcM570XXUkde72t3eA+FJ5Kfohu2W
9XX5NPrX++382f2m/qH//wAAABYAMQBLAGYAfgCXALAAyADgAPgBEQEpAUEBWQFxAYoBowG9AdYB8AIK
AiQCPgJZAnQCkAKsAskC5gMEAyIDQANfA38DngPAA+IEBAQnBEoEbwSWBMAE7wUfBVEFhAW3BesGIAZW
BowGxAb8BzYHcQeuB+wIKghqCKsI7gkxCXYJvQoFCk8KmwroCzYLiAvbDC4MhgzfDTkNlg31DlUOuA8a
D4EP6xBWEMIRNBGnEhwSlRMQE4sUDBSLFQ4VlRYeFqsXORfIGFsY8BmJGiMavhtcG/4coR1FHekejh83
H94giyE0Id8iiyNeJD8lKCYOJvQn3ijHKbQqnCuILHYtYy5RL0AwLzEiMhQzCTQBNPc17zbqN+g46Tnu
OvQ8BT0YPis/QEBYQWtCgUOaRLFFzEblSAFJH0o/S2FMiE2sTtZQBFEzUmBTl1TSVgtXTVh4WYRak1ur
XL5d3F78YBZhOmJgY45kvWXqZxxoUmmKasJsAW0+bndvs3DwcihzZ3Shddl3EnhLeYV633xiffd/j4En
gr+EWIX5h5uJO4rhjI6OPI/tkaCTV5UZluWYrpp+nFWeMqAOof6kKKZfqJKqxa0Br0Kxg7PQti+4krsC
vXnAB8KUxTbH3sqSzV/QHtL81ebYxNut3rnhxuTf6I/r2e6c8Ofy1PRz9fj3tP//AAAAJQBPAHgAoADH
AOwBEgE2AVwBgAGlAcsB8QIYAj8CZwKPArkC5AMQAz4DbAOdA84EAgQ3BG4EqATjBSIFYwWlBegGLQZz
BrsHBQdSB6EH8ghFCJsI8wlNCasKCwpuCtQLPAusDBsMjw0GDYAN/g6ADwIPihAXEKIRLRG5EkcS3BNx
FAsUpRVFFegWkRc7F+YYlxlLGgEauht2HDcc+B27Hn0fRCALINYhnyJrI04kPSU0JionIigdKRwqHSse
LCctLi46L0kwWTFwMogzpjTDNeQ3Cjg0OWM6mzv3PWk+4EBkQeVDckUBRpZILEnGS19M+k6PUChRvFNL
VOBWb1f+WYVbElygXjhfy2FjYv9koWZAZ+NpimszbOJui3A5ceVzmHVFdvV4qHpdfAR9pH9KgO6CkoQ0
hdyHg4kkisaMa44Nj62RSJLhlH6WHpe7mVWa8ZyOniufwqFhovuklaY2p9KpZ6sFrJmuM6/LsWmy+LSV
tiy3xLlduvS8i74ov8nBZ8MHxKTGT8f5yZ7LTM0Czp3QG9Ge0yrUv9Zn2AjZudtr3Q7eruBX4ezjbeTk
5lPnuekU6lDrg+yt7cbu0e/U8MbxtfKQ82z0LPTr9Z72SPby94j4HPiv+S/5qvok+p77Avtm+8r8LvyH
/NH9G/1l/a/9+v5N/rn/Jv+S//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AABtbHVjAAAAAAAAABIAAAAMbmJOTwAAABIAAADocHRQVAAAABYAAAD6c3ZTRQAAABAAAAEQZmlGSQAA
ABAAAAEgZGFESwAAABwAAAEwemhDTgAAAAwAAAFMZnJGUgAAABYAAAFYamFKUAAAAA4AAAFuZW5VUwAA
ABIAAAF8cGxQTAAAABIAAAGOcHRCUgAAABgAAAGgZXNFUwAAABIAAAG4emhUVwAAAA4AAAHKcnVSVQAA
ACQAAAHYa29LUgAAAAwAAAH8ZGVERQAAABAAAAIIbmxOTAAAABYAAAIYaXRJVAAAABQAAAIuAEYAYQBy
AGcAZQAtAEwAQwBEAEwAQwBEACAAYQAgAEMAbwByAGUAcwBGAOQAcgBnAC0ATABDAEQAVgDkAHIAaQAt
AEwAQwBEAEwAQwBEAC0AZgBhAHIAdgBlAHMAawDmAHIAbV9pgnIAIABMAEMARABMAEMARAAgAGMAbwB1
AGwAZQB1AHIwqzDpMPwAIABMAEMARABDAG8AbABvAHIAIABMAEMARABLAG8AbABvAHIAIABMAEMARABM
AEMARAAgAEMAbwBsAG8AcgBpAGQAbwBMAEMARAAgAGMAbwBsAG8Acl9pgnJtsmZ2mG95OlZoBCYEMgQ1
BEIEPQQ+BDkAIAQWBBoALQQ0BDgEQQQ/BDsENQQ5zuy37AAgAEwAQwBEAEYAYQByAGIALQBMAEMARABL
AGwAZQB1AHIAZQBuAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAaQAAbW1vZAAAAAAAAAYQAACchAAA
AADDleyhAAAAAAAAAAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlLCBJbmMuLCAyMDA5AA</bytes>
</object>
</object>
</array>
</array>
<object class="NSColor" key="NSColor" id="704877333">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwAA</bytes>
</object>
</object>
<reference key="NSAlternateImage" ref="671804640"/>
<int key="NSPeriodicDelay">400</int>
<int key="NSPeriodicInterval">75</int>
</object>
</array>
<string key="NSCellSize">{82, 18}</string>
<string key="NSIntercellSpacing">{4, 2}</string>
<int key="NSMatrixFlags">1151868928</int>
<string key="NSCellClass">NSActionCell</string>
<object class="NSButtonCell" key="NSProtoCell" id="541574678">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Radio</string>
<reference key="NSSupport" ref="385894065"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">0</int>
<object class="NSImage" key="NSNormalImage">
<int key="NSImageFlags">549453824</int>
<string key="NSSize">{18, 18}</string>
<array class="NSMutableArray" key="NSReps">
<array>
<integer value="0"/>
<object class="NSBitmapImageRep">
<object class="NSData" key="NSTIFFRepresentation">
<bytes key="NS.bytes">TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw
IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/
29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5
dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA
AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG
AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/
0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/
7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/
5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/
3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD
AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns
AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/
6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/
/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/
///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl
YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA
AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD
AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu
AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQEAAAMAAAABABIAAAEB
AAMAAAABABIAAAECAAMAAAAEAAAFugEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES
AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS
AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA</bytes>
</object>
</object>
</array>
</array>
<reference key="NSColor" ref="704877333"/>
</object>
<reference key="NSAlternateImage" ref="671804640"/>
<int key="NSPeriodicDelay">400</int>
<int key="NSPeriodicInterval">75</int>
</object>
<reference key="NSSelectedCell" ref="165264936"/>
<reference key="NSBackgroundColor" ref="976045936"/>
<object class="NSColor" key="NSCellBackgroundColor" id="129773243">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<reference key="NSFont" ref="385894065"/>
</object>
<object class="NSTextField" id="51944759">
<reference key="NSNextResponder" ref="1005"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{17, 260}, {84, 17}}</string>
<reference key="NSSuperview" ref="1005"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="901629501"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="183918644">
<int key="NSCellFlags">68288064</int>
<int key="NSCellFlags2">272630784</int>
<string key="NSContents">Show player:</string>
<reference key="NSSupport" ref="385894065"/>
<reference key="NSControlView" ref="51944759"/>
<reference key="NSBackgroundColor" ref="976045936"/>
<reference key="NSTextColor" ref="522026884"/>
</object>
</object>
<object class="NSTextField" id="901629501">
<reference key="NSNextResponder" ref="1005"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{106, 258}, {36, 22}}</string>
<reference key="NSSuperview" ref="1005"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="983722867"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="324146376">
<int key="NSCellFlags">-1804468671</int>
<int key="NSCellFlags2">-1874852864</int>
<string key="NSContents"/>
<reference key="NSSupport" ref="385894065"/>
<object class="NSNumberFormatter" key="NSFormatter" id="733863002">
<dictionary class="NSMutableDictionary" key="NS.attributes">
<boolean value="NO" key="allowsFloats"/>
<integer value="1040" key="formatterBehavior"/>
<object class="NSLocale" key="locale">
<string key="NS.identifier"/>
</object>
<real value="4" key="maximum"/>
<integer value="1" key="maximumIntegerDigits"/>
<real value="1" key="minimum"/>
<integer value="1" key="minimumIntegerDigits"/>
<real value="1" key="roundingIncrement"/>
</dictionary>
<string key="NS.positiveformat">#1</string>
<string key="NS.negativeformat">#1</string>
<nil key="NS.positiveattrs"/>
<nil key="NS.negativeattrs"/>
<nil key="NS.zero"/>
<nil key="NS.nil"/>
<object class="NSAttributedString" key="NS.nan">
<string key="NSString">NaN</string>
<dictionary key="NSAttributes"/>
</object>
<real value="1" key="NS.min"/>
<real value="4" key="NS.max"/>
<object class="NSDecimalNumberHandler" key="NS.rounding">
<int key="NS.roundingmode">3</int>
<bool key="NS.raise.overflow">YES</bool>
<bool key="NS.raise.underflow">YES</bool>
<bool key="NS.raise.dividebyzero">YES</bool>
</object>
<string key="NS.decimal">.</string>
<string key="NS.thousand">,</string>
<bool key="NS.hasthousands">NO</bool>
<bool key="NS.localized">NO</bool>
<bool key="NS.allowsfloats">NO</bool>
</object>
<reference key="NSControlView" ref="901629501"/>
<bool key="NSDrawsBackground">YES</bool>
<object class="NSColor" key="NSBackgroundColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textBackgroundColor</string>
<reference key="NSColor" ref="129773243"/>
</object>
<object class="NSColor" key="NSTextColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textColor</string>
<reference key="NSColor" ref="909323645"/>
</object>
</object>
</object>
<object class="NSStepper" id="983722867">
<reference key="NSNextResponder" ref="1005"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{147, 255}, {19, 27}}</string>
<reference key="NSSuperview" ref="1005"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="529444683"/>
<bool key="NSEnabled">YES</bool>
<object class="NSStepperCell" key="NSCell" id="180178058">
<int key="NSCellFlags">654848</int>
<int key="NSCellFlags2">0</int>
<reference key="NSControlView" ref="983722867"/>
<double key="NSValue">1</double>
<double key="NSMinValue">1</double>
<double key="NSMaxValue">4</double>
<double key="NSIncrement">1</double>
<bool key="NSValueWraps">YES</bool>
</object>
</object>
</array>
<string key="NSFrameSize">{600, 300}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="51944759"/>
<string key="NSClassName">OEGameControllerView</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="1005"/>
</object>
<int key="connectionID">2</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showedBindingsChanged:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="806786179"/>
</object>
<int key="connectionID">9</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">bindingType</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="806786179"/>
</object>
<int key="connectionID">10</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">playerField</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="901629501"/>
</object>
<int key="connectionID">24</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">playerStepper</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="983722867"/>
</object>
<int key="connectionID">25</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showedBindingsChanged:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="983722867"/>
</object>
<int key="connectionID">26</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showedBindingsChanged:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="901629501"/>
</object>
<int key="connectionID">27</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="1001"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="1003"/>
<reference key="parent" ref="0"/>
<string key="objectName">First Responder</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-3</int>
<reference key="object" ref="1004"/>
<reference key="parent" ref="0"/>
<string key="objectName">Application</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="1005"/>
<array class="NSMutableArray" key="children">
<reference ref="529444683"/>
<reference ref="806786179"/>
<reference ref="51944759"/>
<reference ref="901629501"/>
<reference ref="983722867"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">3</int>
<reference key="object" ref="529444683"/>
<array class="NSMutableArray" key="children">
<reference ref="262381886"/>
</array>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="806786179"/>
<array class="NSMutableArray" key="children">
<reference ref="223753203"/>
<reference ref="541574678"/>
<reference ref="165264936"/>
</array>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">5</int>
<reference key="object" ref="223753203"/>
<reference key="parent" ref="806786179"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">6</int>
<reference key="object" ref="541574678"/>
<reference key="parent" ref="806786179"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">7</int>
<reference key="object" ref="165264936"/>
<reference key="parent" ref="806786179"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="262381886"/>
<reference key="parent" ref="529444683"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">15</int>
<reference key="object" ref="51944759"/>
<array class="NSMutableArray" key="children">
<reference ref="183918644"/>
</array>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">16</int>
<reference key="object" ref="901629501"/>
<array class="NSMutableArray" key="children">
<reference ref="324146376"/>
</array>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">17</int>
<reference key="object" ref="983722867"/>
<array class="NSMutableArray" key="children">
<reference ref="180178058"/>
</array>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">18</int>
<reference key="object" ref="180178058"/>
<reference key="parent" ref="983722867"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">19</int>
<reference key="object" ref="324146376"/>
<array class="NSMutableArray" key="children">
<reference ref="733863002"/>
</array>
<reference key="parent" ref="901629501"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">20</int>
<reference key="object" ref="733863002"/>
<reference key="parent" ref="324146376"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">21</int>
<reference key="object" ref="183918644"/>
<reference key="parent" ref="51944759"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="15.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="16.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="17.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="18.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="19.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES" key="20.IBNumberFormatterLocalizesFormatMetadataKey"/>
<string key="20.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="21.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="4.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="5.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="6.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="7.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="8.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">27</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">OEGameControllerView</string>
<string key="superclassName">NSView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/OEGameControllerView.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<real value="1060" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
<real value="4200" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
</data>
</archive>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17503.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17503.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="GBAControlsPreference">
<connections>
<outlet property="bindingType" destination="4" id="10"/>
<outlet property="playerField" destination="16" id="24"/>
<outlet property="playerStepper" destination="17" id="25"/>
<outlet property="view" destination="1" id="2"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customView id="1" customClass="OEGameControllerView">
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="17">
<rect key="frame" x="147" y="255" width="19" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="4" doubleValue="1" autorepeat="NO" valueWraps="YES" id="18"/>
<connections>
<action selector="showedBindingsChanged:" target="-2" id="26"/>
</connections>
</stepper>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="16">
<rect key="frame" x="106" y="258" width="36" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="19">
<numberFormatter key="formatter" formatterBehavior="custom10_4" allowsFloats="NO" usesGroupingSeparator="NO" formatWidth="-1" groupingSize="0" minimumIntegerDigits="1" maximumIntegerDigits="1" id="20">
<real key="roundingIncrement" value="1"/>
<nil key="negativeInfinitySymbol"/>
<nil key="positiveInfinitySymbol"/>
<real key="minimum" value="1"/>
<real key="maximum" value="4"/>
</numberFormatter>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="showedBindingsChanged:" target="-2" id="27"/>
</connections>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="15">
<rect key="frame" x="17" y="260" width="84" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Show player:" id="21">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<matrix verticalHuggingPriority="750" fixedFrame="YES" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4">
<rect key="frame" x="274" y="259" width="168" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="82" height="18"/>
<size key="intercellSpacing" width="4" height="2"/>
<buttonCell key="prototype" type="radio" title="Radio" imagePosition="leading" alignment="left" inset="2" id="6">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="Keyboard" imagePosition="leading" alignment="left" state="on" inset="2" id="7">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</column>
<column>
<buttonCell type="radio" title="Gamepad" imagePosition="leading" alignment="left" tag="1" inset="2" id="5">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</column>
</cells>
<connections>
<action selector="showedBindingsChanged:" target="-2" id="9"/>
</connections>
</matrix>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3">
<rect key="frame" x="168" y="260" width="101" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Show bindings:" id="8">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="21" y="154"/>
</customView>
</objects>
</document>
-367
View File
@@ -1,367 +0,0 @@
#ifndef _MSC_VER
#include <stdbool.h>
#endif
#include <sched.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef __CELLOS_LV2__
//#include <malloc.h>
#endif
//#ifdef _MSC_VER
//#define snprintf _snprintf
//#pragma pack(1)
//#endif
#include "libretro.h"
#include "Settings.hxx"
#include "TIA.hxx"
#include "Props.hxx"
#include "PropsSet.hxx"
#include "Cart.hxx"
#include "Console.hxx"
#include "Serializer.hxx"
#include "Event.hxx"
#include "Switches.hxx"
#include "MD5.hxx"
#include "SoundSDL.hxx"
#include "Paddles.hxx"
struct Stella
{
Console* GameConsole;
Settings GameSettings;
const uInt32* Palette;
SoundSDL Sound;
Stella() {GameConsole = 0; Palette = 0;}
~Stella() {delete GameConsole;}
};
Stella* stella;
static uint32_t frame_buffer[256*160];
uint8_t samplebuffer[2048];
//Set the palette for the current stella instance
void stellaMDFNSetPalette (const uInt32* palette)
{
if(stella)
{
stella->Palette = palette;
}
}
//Get the settings from the current stella instance
Settings& stellaMDFNSettings ()
{
if(stella)
{
return stella->GameSettings;
}
//HACK
abort();
}
static retro_video_refresh_t video_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
static retro_environment_t environ_cb;
static retro_audio_sample_t audio_cb;
static retro_audio_sample_batch_t audio_batch_cb;
void retro_set_environment(retro_environment_t cb) { environ_cb = cb; }
void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
void retro_set_audio_sample(retro_audio_sample_t cb) { audio_cb = cb; }
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
static void update_input()
{
if (!input_poll_cb)
return;
input_poll_cb();
//Update stella's event structure
stella->GameConsole->event().set(Event::JoystickZeroUp, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP));
stella->GameConsole->event().set(Event::JoystickZeroDown, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN));
stella->GameConsole->event().set(Event::JoystickZeroLeft, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT));
stella->GameConsole->event().set(Event::JoystickZeroRight, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT));
stella->GameConsole->event().set(Event::JoystickZeroFire1, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B));
stella->GameConsole->event().set(Event::JoystickZeroFire2, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A));
stella->GameConsole->event().set(Event::JoystickZeroFire3, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X));
stella->GameConsole->event().set(Event::ConsoleLeftDiffA, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L));
stella->GameConsole->event().set(Event::ConsoleLeftDiffB, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2));
stella->GameConsole->event().set(Event::ConsoleColor, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3));
stella->GameConsole->event().set(Event::ConsoleRightDiffA, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R));
stella->GameConsole->event().set(Event::ConsoleRightDiffB, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2));
stella->GameConsole->event().set(Event::ConsoleBlackWhite, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3));
stella->GameConsole->event().set(Event::ConsoleSelect, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT));
stella->GameConsole->event().set(Event::ConsoleReset, input_state_cb(Controller::Left, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START));
//Events for right player's joystick
stella->GameConsole->event().set(Event::JoystickOneUp, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP));
stella->GameConsole->event().set(Event::JoystickOneDown, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN));
stella->GameConsole->event().set(Event::JoystickOneLeft, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT));
stella->GameConsole->event().set(Event::JoystickOneRight, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT));
stella->GameConsole->event().set(Event::JoystickOneFire1, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B));
stella->GameConsole->event().set(Event::JoystickOneFire2, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A));
stella->GameConsole->event().set(Event::JoystickOneFire3, input_state_cb(Controller::Right, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X));
//stella->GameConsole->fry();
//Tell all input devices to read their state from the event structure
stella->GameConsole->switches().update();
stella->GameConsole->controller(Controller::Left).update();
stella->GameConsole->controller(Controller::Right).update();
}
/************************************
* libretro implementation
************************************/
static struct retro_system_av_info g_av_info;
void retro_get_system_info(struct retro_system_info *info)
{
memset(info, 0, sizeof(*info));
info->library_name = "Stella";
info->library_version = "3.4.1";
info->need_fullpath = true;
info->valid_extensions = "a26|A26";
}
void retro_get_system_av_info(struct retro_system_av_info *info)
{
memset(info, 0, sizeof(*info));
// Just assume NTSC for now. TODO: Verify FPS.
info->timing.fps = stella->GameConsole->getFramerate();
info->timing.sample_rate = 31400;
info->geometry.base_width = 160;
info->geometry.base_height = 210;
info->geometry.max_width = 160;
info->geometry.max_height = 256;
info->geometry.aspect_ratio = 4.0 / 3.0;
}
void retro_set_controller_port_device(unsigned port, unsigned device)
{
(void)port;
(void)device;
}
size_t retro_serialize_size(void)
{
return 0xfc080;
//return 0;
}
bool retro_serialize(void *data, size_t size)
{
//if (size != STATE_SIZE)
// return false;
//Serializer state((uint8_t*)data);
//Serializer(const string& filename, bool readonly = false);
//Serializer state((const string&) data);
//stella->GameConsole->save((Serializer&)state);
Serializer state((StateMem *)data);
stella->GameConsole->save(state);
return false;
}
bool retro_unserialize(const void *data, size_t size)
{
//if (size != STATE_SIZE)
// return false;
//Serializer state((uint8_t*)data);
//stella->GameConsole->load(state);
//Serializer state((const string&) data);
//stella->GameConsole->load((Serializer&)state);
Serializer state((StateMem *)data);
stella->GameConsole->load(state);
return true;
}
void retro_cheat_reset(void)
{}
void retro_cheat_set(unsigned index, bool enabled, const char *code)
{
(void)index;
(void)enabled;
(void)code;
}
bool retro_load_game(const struct retro_game_info *info)
{
stella = new Stella();
const char *full_path;
full_path = info->path;
//Get the game properties
string cartMD5 = MD5((const uInt8*)info->data, info->size);
PropertiesSet propslist(0);
Properties gameProperties;
propslist.getMD5(cartMD5, gameProperties);
//Input - Set paddles for games that require them (Range: 1-10)
Paddles::setDigitalSensitivity(5);
Paddles::setMouseSensitivity(5);
//Load the cart
string cartType = gameProperties.get(Cartridge_Type);
string cartID = "";
Cartridge* stellaCart = Cartridge::create((const uInt8*)info->data, (uInt32)info->size, cartMD5, cartType, cartID, stella->GameSettings);
//printf("%s %d\n", cartMD5.c_str(), info->size);
if(stellaCart == 0)
{
printf("Stella: Failed to load cartridge.");
return false;
}
//Create the console
stella->GameConsole = new Console(stella->Sound, stellaCart, gameProperties);
//Init sound
stella->Sound.open();
//stella->Palette = 0; //NTSC
return true;
}
bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
{
(void)game_type;
(void)info;
(void)num_info;
return false;
}
void retro_unload_game(void)
{
}
unsigned retro_get_region(void)
{
//stella->GameConsole->getFramerate();
return RETRO_REGION_NTSC;
}
unsigned retro_api_version(void)
{
return RETRO_API_VERSION;
}
void *retro_get_memory_data(unsigned id)
{
return NULL;
}
size_t retro_get_memory_size(unsigned id)
{
return 0;
}
void retro_init(void)
{
unsigned level = 3;
environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
}
void retro_deinit(void)
{
delete stella;
stella = 0;
//free(frame_buffer);
}
void retro_reset(void)
{
stella->GameConsole->system().reset();
}
void retro_run(void)
{
//INPUT
update_input();
//EMULATE
stella->GameConsole->tia().update();
//VIDEO
//Get the frame info from stella
Int32 frameWidth = stella->GameConsole->tia().width();
Int32 frameHeight = stella->GameConsole->tia().height();
//Copy the frame from stella to video buffer
for ( unsigned int i = 0; i < frameHeight * frameWidth; ++i )
frame_buffer[i] = stella->Palette[stella->GameConsole->tia().currentFrameBuffer()[i]];
video_cb(frame_buffer, frameWidth, frameHeight, frameWidth << 2);
//AUDIO
//Get the number of samples in a frame
uint32_t soundFrameSize = 31400.0f / stella->GameConsole->getFramerate();
//Process one frame of audio from stella
//const int16_t * final_samplebuffer;
stella->Sound.processFragment(samplebuffer, soundFrameSize);
int16_t sample;
//Convert and stash it in the resampler...
for(int i = 0; i != soundFrameSize; i ++)
{
sample = (samplebuffer[i] << 8) - 32768;
int16_t frame[2] = {sample, sample};
//Resampler::Fill(frame, 2);
audio_cb(frame[0], frame[1]);
//audio_batch_cb((const int16_t *)sample, soundFrameSize);
//memcpy(&final_samplebuffer, &sample, 2 * 2);
}
//TODO: fix
//The array you pass in batch callback has to be interleaved signed 16-bit stereo
//audio_batch_cb((const int16_t *)samplebuffer, soundFrameSize);
//audio_batch_cb((const int16_t *)sample, soundFrameSize);
//audio_batch_cb(final_samplebuffer, soundFrameSize);
/* convert uint16_t to uint8_t */
//uint16_t A = 120;
//uint8_t B;
//B = (uint8_t)A; // Get lower byte of 16-bit var
//B = (uint8_t)(A >> 8); // Get upper byte of 16-bit var
/* convert 2 uint8_t to uint16_t */
//uint8_t d1=0x01;
//uint8_t d2=0x02;
//uint16_t wd;
//union {
// uint8_t bytes[2];
// int n;
//} join;
//join.bytes[0] = d1;
//join.bytes[1] = d2;
//wd = join.n;
//while(1) {
//}
}
-333
View File
@@ -1,333 +0,0 @@
#ifndef LIBRETRO_H__
#define LIBRETRO_H__
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#else
#if defined(_MSC_VER) && !defined(__cplusplus)
#define bool unsigned char
#define true 1
#define false 0
#else
#include <stdbool.h>
#endif
#endif
#define RETRO_API_VERSION 1
#define RETRO_DEVICE_MASK 0xff
#define RETRO_DEVICE_NONE 0
#define RETRO_DEVICE_JOYPAD 1
#define RETRO_DEVICE_MOUSE 2
#define RETRO_DEVICE_KEYBOARD 3
#define RETRO_DEVICE_LIGHTGUN 4
#define RETRO_DEVICE_ANALOG 5
#define RETRO_DEVICE_JOYPAD_MULTITAP ((1 << 8) | RETRO_DEVICE_JOYPAD)
#define RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE ((1 << 8) | RETRO_DEVICE_LIGHTGUN)
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIER ((2 << 8) | RETRO_DEVICE_LIGHTGUN)
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS ((3 << 8) | RETRO_DEVICE_LIGHTGUN)
#define RETRO_DEVICE_ID_JOYPAD_B 0
#define RETRO_DEVICE_ID_JOYPAD_Y 1
#define RETRO_DEVICE_ID_JOYPAD_SELECT 2
#define RETRO_DEVICE_ID_JOYPAD_START 3
#define RETRO_DEVICE_ID_JOYPAD_UP 4
#define RETRO_DEVICE_ID_JOYPAD_DOWN 5
#define RETRO_DEVICE_ID_JOYPAD_LEFT 6
#define RETRO_DEVICE_ID_JOYPAD_RIGHT 7
#define RETRO_DEVICE_ID_JOYPAD_A 8
#define RETRO_DEVICE_ID_JOYPAD_X 9
#define RETRO_DEVICE_ID_JOYPAD_L 10
#define RETRO_DEVICE_ID_JOYPAD_R 11
#define RETRO_DEVICE_ID_JOYPAD_L2 12
#define RETRO_DEVICE_ID_JOYPAD_R2 13
#define RETRO_DEVICE_ID_JOYPAD_L3 14
#define RETRO_DEVICE_ID_JOYPAD_R3 15
#define RETRO_DEVICE_INDEX_ANALOG_LEFT 0
#define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1
#define RETRO_DEVICE_ID_ANALOG_X 0
#define RETRO_DEVICE_ID_ANALOG_Y 1
#define RETRO_DEVICE_ID_MOUSE_X 0
#define RETRO_DEVICE_ID_MOUSE_Y 1
#define RETRO_DEVICE_ID_MOUSE_LEFT 2
#define RETRO_DEVICE_ID_MOUSE_RIGHT 3
#define RETRO_DEVICE_ID_LIGHTGUN_X 0
#define RETRO_DEVICE_ID_LIGHTGUN_Y 1
#define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2
#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3
#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4
#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5
#define RETRO_DEVICE_ID_LIGHTGUN_START 6
#define RETRO_REGION_NTSC 0
#define RETRO_REGION_PAL 1
#define RETRO_MEMORY_MASK 0xff
#define RETRO_MEMORY_SAVE_RAM 0
#define RETRO_MEMORY_RTC 1
#define RETRO_MEMORY_SYSTEM_RAM 2
#define RETRO_MEMORY_VIDEO_RAM 3
#define RETRO_MEMORY_SNES_BSX_RAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_BSX_PRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM ((4 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_GAME_BOY_RAM ((5 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_GAME_BOY_RTC ((6 << 8) | RETRO_MEMORY_RTC)
#define RETRO_GAME_TYPE_BSX 0x101
#define RETRO_GAME_TYPE_BSX_SLOTTED 0x102
#define RETRO_GAME_TYPE_SUFAMI_TURBO 0x103
#define RETRO_GAME_TYPE_SUPER_GAME_BOY 0x104
// Environment commands.
#define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * --
// Sets screen rotation of graphics.
// Is only implemented if rotation can be accelerated by hardware.
// Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, 270 degrees
// counter-clockwise respectively.
//
#define RETRO_ENVIRONMENT_GET_OVERSCAN 2 // bool * --
// Boolean value whether or not the implementation should use overscan, or crop away overscan.
//
#define RETRO_ENVIRONMENT_GET_CAN_DUPE 3 // bool * --
// Boolean value whether or not RetroArch supports frame duping,
// passing NULL to video frame callback.
//
#define RETRO_ENVIRONMENT_GET_VARIABLE 4 // struct retro_variable * --
// Interface to aquire user-defined information from environment
// that cannot feasibly be supported in a multi-system way.
// Mostly used for obscure,
// specific features that the user can tap into when neseccary.
//
#define RETRO_ENVIRONMENT_SET_VARIABLES 5 // const struct retro_variable * --
// Allows an implementation to signal the environment
// which variables it might want to check for later using GET_VARIABLE.
// 'data' points to an array of retro_variable structs terminated by a { NULL, NULL } element.
// retro_variable::value should contain a human readable description of the key.
//
#define RETRO_ENVIRONMENT_SET_MESSAGE 6 // const struct retro_message * --
// Sets a message to be displayed in implementation-specific manner for a certain amount of 'frames'.
// Should not be used for trivial messages, which should simply be logged to stderr.
#define RETRO_ENVIRONMENT_SHUTDOWN 7 // N/A (NULL) --
// Requests the frontend to shutdown.
// Should only be used if game has a specific
// way to shutdown the game from a menu item or similar.
//
#define RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL 8
// const unsigned * --
// Gives a hint to the frontend how demanding this implementation
// is on a system. E.g. reporting a level of 2 means
// this implementation should run decently on all frontends
// of level 2 and up.
//
// It can be used by the frontend to potentially warn
// about too demanding implementations.
//
// The levels are "floating", but roughly defined as:
// 1: Low-powered devices such as Raspberry Pi, smart phones, tablets, etc.
// 2: Medium-spec consoles, such as PS3/360, with sub-par CPUs.
// 3: Modern desktop/laptops with reasonably powerful CPUs.
// 4: High-end desktops with very powerful CPUs.
//
// This function can be called on a per-game basis,
// as certain games an implementation can play might be
// particularily demanding.
// If called, it should be called in retro_load_game().
//
#define RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY 9
// const char ** --
// Returns the "system" directory of the frontend.
// This directory can be used to store system specific ROMs such as BIOSes, configuration data, etc.
// The returned value can be NULL.
// If so, no such directory is defined,
// and it's up to the implementation to find a suitable directory.
//
#define RETRO_ENVIRONMENT_SET_PIXEL_FORMAT 10
// const enum retro_pixel_format * --
// Sets the internal pixel format used by the implementation.
// The default pixel format is RETRO_PIXEL_FORMAT_XRGB1555.
// If the call returns false, the frontend does not support this pixel format.
// This function should be called inside retro_load_game() or retro_get_system_av_info().
enum retro_pixel_format
{
RETRO_PIXEL_FORMAT_0RGB1555 = 0, // 0RGB1555, native endian. 0 bit must be set to 0.
RETRO_PIXEL_FORMAT_XRGB8888 // XRGB8888, native endian. X bits are ignored.
};
struct retro_message
{
const char *msg; // Message to be displayed.
unsigned frames; // Duration in frames of message.
};
struct retro_system_info
{
const char *library_name; // Descriptive name of library. Should not contain any version numbers, etc.
const char *library_version; // Descriptive version of core.
const char *valid_extensions; // A string listing probably rom extensions the core will be able to load, separated with pipe.
// I.e. "bin|rom|iso".
// Typically used for a GUI to filter out extensions.
bool need_fullpath; // If true, retro_load_game() is guaranteed to provide a valid pathname in retro_game_info::path.
// ::data and ::size are both invalid.
// If false, ::data and ::size are guaranteed to be valid, but ::path might not be valid.
// This is typically set to true for libretro implementations that must load from file.
// Implementations should strive for setting this to false, as it allows the frontend to perform patching, etc.
bool block_extract; // If true, the frontend is not allowed to extract any archives before loading the real ROM.
// Necessary for certain libretro implementations that load games from zipped archives.
};
struct retro_game_geometry
{
unsigned base_width; // Nominal video width of game.
unsigned base_height; // Nominal video height of game.
unsigned max_width; // Maximum possible width of game.
unsigned max_height; // Maximum possible height of game.
float aspect_ratio; // Nominal aspect ratio of game. If aspect_ratio is <= 0.0,
// an aspect ratio of base_width / base_height is assumed.
// A frontend could override this setting if desired.
};
struct retro_system_timing
{
double fps; // FPS of video content.
double sample_rate; // Sampling rate of audio.
};
struct retro_system_av_info
{
struct retro_game_geometry geometry;
struct retro_system_timing timing;
};
struct retro_variable
{
const char *key; // Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE.
// If NULL, obtains the complete environment string if more complex parsing is necessary.
// The environment string is formatted as key-value pairs delimited by semicolons as so:
// "key1=value1;key2=value2;..."
const char *value; // Value to be obtained. If key does not exist, it is set to NULL.
};
struct retro_game_info
{
const char *path; // Path to game, UTF-8 encoded. Usually used as a reference.
// May be NULL if rom was loaded from stdin or similar.
// retro_system_info::need_fullpath guaranteed that this path is valid.
const void *data; // Memory buffer of loaded game. Will be NULL if need_fullpath was set.
size_t size; // Size of memory buffer.
const char *meta; // String of implementation specific meta-data.
};
// Callbacks
//
// Environment callback. Gives implementations a way of performing uncommon tasks. Extensible.
typedef bool (*retro_environment_t)(unsigned cmd, void *data);
// Render a frame. Pixel format is 15-bit 0RGB1555 native endian unless changed (see RETRO_ENVIRONMENT_SET_PIXEL_FORMAT).
// Width and height specify dimensions of buffer.
// Pitch specifices length in bytes between two lines in buffer.
typedef void (*retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch);
// Renders a single audio frame. Should only be used if implementation generates a single sample at a time.
// Format is signed 16-bit native endian.
typedef void (*retro_audio_sample_t)(int16_t left, int16_t right);
// Renders multiple audio frames in one go. One frame is defined as a sample of left and right channels, interleaved.
// I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames.
// Only one of the audio callbacks must ever be used.
typedef size_t (*retro_audio_sample_batch_t)(const int16_t *data, size_t frames);
// Polls input.
typedef void (*retro_input_poll_t)(void);
// Queries for input for player 'port'. device will be masked with RETRO_DEVICE_MASK.
// Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that have been set with retro_set_controller_port_device()
// will still use the higher level RETRO_DEVICE_JOYPAD to request input.
typedef int16_t (*retro_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id);
// Sets callbacks. retro_set_environment() is guaranteed to be called before retro_init().
// The rest of the set_* functions are guaranteed to have been called before the first call to retro_run() is made.
void retro_set_environment(retro_environment_t);
void retro_set_video_refresh(retro_video_refresh_t);
void retro_set_audio_sample(retro_audio_sample_t);
void retro_set_audio_sample_batch(retro_audio_sample_batch_t);
void retro_set_input_poll(retro_input_poll_t);
void retro_set_input_state(retro_input_state_t);
// Library global initialization/deinitialization.
void retro_init(void);
void retro_deinit(void);
// Must return RETRO_API_VERSION. Used to validate ABI compatibility when the API is revised.
unsigned retro_api_version(void);
// Gets statically known system info. Pointers provided in *info must be statically allocated.
// Can be called at any time, even before retro_init().
void retro_get_system_info(struct retro_system_info *info);
// Gets information about system audio/video timings and geometry.
// Can be called only after retro_load_game() has successfully completed.
void retro_get_system_av_info(struct retro_system_av_info *info);
// Sets device to be used for player 'port'.
void retro_set_controller_port_device(unsigned port, unsigned device);
// Resets the current game.
void retro_reset(void);
// Runs the game for one video frame.
// During retro_run(), input_poll callback must be called at least once.
//
// If a frame is not rendered for reasons where a game "dropped" a frame,
// this still counts as a frame, and retro_run() should explicitly dupe a frame if GET_CAN_DUPE returns true.
// In this case, the video callback can take a NULL argument for data.
void retro_run(void);
// Returns the amount of data the implementation requires to serialize internal state (save states).
// Beetween calls to retro_load_game() and retro_unload_game(), the returned size is never allowed to be larger than a previous returned value, to
// ensure that the frontend can allocate a save state buffer once.
size_t retro_serialize_size(void);
// Serializes internal state. If failed, or size is lower than retro_serialize_size(), it should return false, true otherwise.
bool retro_serialize(void *data, size_t size);
bool retro_unserialize(const void *data, size_t size);
void retro_cheat_reset(void);
void retro_cheat_set(unsigned index, bool enabled, const char *code);
// Loads a game.
bool retro_load_game(const struct retro_game_info *game);
// Loads a "special" kind of game. Should not be used except in extreme cases.
bool retro_load_game_special(
unsigned game_type,
const struct retro_game_info *info, size_t num_info
);
// Unloads a currently loaded game.
void retro_unload_game(void);
// Gets region of game.
unsigned retro_get_region(void);
// Gets region of memory.
void *retro_get_memory_data(unsigned id);
size_t retro_get_memory_size(unsigned id);
#ifdef __cplusplus
}
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: BankRomCheat.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Console.hxx"
#include "Cart.hxx"
#include "OSystem.hxx"
#include "BankRomCheat.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BankRomCheat::BankRomCheat(OSystem* os, const string& name, const string& code)
: Cheat(os, name, code)
{
if(myCode.length() == 7)
myCode = "0" + code;
bank = unhex(myCode.substr(0, 2));
address = 0xf000 + unhex(myCode.substr(2, 3));
value = unhex(myCode.substr(5, 2));
count = unhex(myCode.substr(7, 1)) + 1;
// Back up original data; we need this if the cheat is ever disabled
for(int i = 0; i < count; ++i)
savedRom[i] = myOSystem->console().cartridge().peek(address + i);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BankRomCheat::~BankRomCheat()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool BankRomCheat::enable()
{
evaluate();
return myEnabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool BankRomCheat::disable()
{
int oldBank = myOSystem->console().cartridge().bank();
myOSystem->console().cartridge().bank(bank);
for(int i = 0; i < count; ++i)
myOSystem->console().cartridge().patch(address + i, savedRom[i]);
myOSystem->console().cartridge().bank(oldBank);
return myEnabled = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BankRomCheat::evaluate()
{
if(!myEnabled)
{
int oldBank = myOSystem->console().cartridge().bank();
myOSystem->console().cartridge().bank(bank);
for(int i = 0; i < count; ++i)
myOSystem->console().cartridge().patch(address + i, value);
myOSystem->console().cartridge().bank(oldBank);
myEnabled = true;
}
}
+44
View File
@@ -0,0 +1,44 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: BankRomCheat.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef BANK_ROM_CHEAT_HXX
#define BANK_ROM_CHEAT_HXX
#include "Cheat.hxx"
class BankRomCheat : public Cheat
{
public:
BankRomCheat(OSystem* os, const string& name, const string& code);
~BankRomCheat();
virtual bool enable();
virtual bool disable();
virtual void evaluate();
private:
uInt8 savedRom[16];
uInt16 address;
uInt8 value;
uInt8 count;
int bank;
};
#endif
+79
View File
@@ -0,0 +1,79 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cheat.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CHEAT_HXX
#define CHEAT_HXX
class OSystem;
#include "StringList.hxx"
#include "bspf.hxx"
class Cheat
{
public:
Cheat(OSystem* osystem, const string& name, const string& code)
: myOSystem(osystem),
myName(name),
myCode(code),
myEnabled(false)
{
if(name == "") myName = code;
myName = StringList::removePattern(myName, "\":");
}
virtual ~Cheat() { }
bool enabled() const { return myEnabled; }
const string& name() const { return myName; }
const string& code() const { return myCode; }
virtual bool enable() = 0;
virtual bool disable() = 0;
virtual void evaluate() = 0;
protected:
static uInt16 unhex(const string& hex)
{
int ret = 0;
for(unsigned int i=0; i<hex.size(); i++) {
char c = hex[i];
ret *= 16;
if(c >= '0' && c <= '9')
ret += c - '0';
else if(c >= 'A' && c <= 'F')
ret += c - 'A' + 10;
else
ret += c - 'a' + 10;
}
return ret;
}
protected:
OSystem* myOSystem;
string myName;
string myCode;
bool myEnabled;
};
#endif
+278
View File
@@ -0,0 +1,278 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CheatCodeDialog.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <sstream>
#include "bspf.hxx"
#include "Cheat.hxx"
#include "CheatManager.hxx"
#include "CheckListWidget.hxx"
#include "DialogContainer.hxx"
#include "Dialog.hxx"
#include "InputTextDialog.hxx"
#include "OSystem.hxx"
#include "Props.hxx"
#include "StringList.hxx"
#include "Widget.hxx"
#include "CheatCodeDialog.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheatCodeDialog::CheatCodeDialog(OSystem* osystem, DialogContainer* parent,
const GUI::Font& font)
: Dialog(osystem, parent, 0, 0, 0, 0)
{
const int lineHeight = font.getLineHeight(),
fontWidth = font.getMaxCharWidth(),
buttonWidth = font.getStringWidth("Defaults") + 20,
buttonHeight = font.getLineHeight() + 4;
int xpos, ypos;
WidgetArray wid;
ButtonWidget* b;
// Set real dimensions
_w = 46 * fontWidth + 10;
_h = 11 * (lineHeight + 4) + 10;
// List of cheats, with checkboxes to enable/disable
xpos = 10; ypos = 10;
myCheatList =
new CheckListWidget(this, font, xpos, ypos, _w - buttonWidth - 25,
_h - 2*buttonHeight - 10);
myCheatList->setEditable(false);
wid.push_back(myCheatList);
xpos += myCheatList->getWidth() + 5; ypos = 15;
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"Add", kAddCheatCmd);
wid.push_back(b);
ypos += lineHeight + 10;
myEditButton =
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"Edit", kEditCheatCmd);
wid.push_back(myEditButton);
ypos += lineHeight + 10;
myRemoveButton =
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"Remove", kRemCheatCmd);
wid.push_back(myRemoveButton);
ypos += lineHeight + 10;
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"One shot", kAddOneShotCmd);
wid.push_back(b);
// Inputbox which will pop up when adding/editing a cheat
StringList labels;
labels.push_back("Name: ");
labels.push_back("Code: ");
myCheatInput = new InputTextDialog(this, font, labels);
myCheatInput->setTarget(this);
addToFocusList(wid);
// Add OK and Cancel buttons
wid.clear();
addOKCancelBGroup(wid, font);
addBGroupToFocusList(wid);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheatCodeDialog::~CheatCodeDialog()
{
delete myCheatInput;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::loadConfig()
{
// Load items from CheatManager
// Note that the items are always in the same order/number as given in
// the CheatManager, so the arrays will be one-to-one
StringList l;
BoolArray b;
const CheatList& list = instance().cheat().list();
for(unsigned int i = 0; i < list.size(); ++i)
{
l.push_back(list[i]->name());
b.push_back(bool(list[i]->enabled()));
}
myCheatList->setList(l, b);
// Redraw the list, auto-selecting the first item if possible
myCheatList->setSelected(l.size() > 0 ? 0 : -1);
bool enabled = (list.size() > 0);
myEditButton->setEnabled(enabled);
myRemoveButton->setEnabled(enabled);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::saveConfig()
{
// Inspect checkboxes for enable/disable codes
const CheatList& list = instance().cheat().list();
for(unsigned int i = 0; i < myCheatList->getList().size(); ++i)
{
if(myCheatList->getState(i))
list[i]->enable();
else
list[i]->disable();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::addCheat()
{
myCheatInput->show(); // Center input dialog over entire screen
myCheatInput->setText("", 0);
myCheatInput->setText("", 1);
myCheatInput->setTitle("");
myCheatInput->setFocus(0);
myCheatInput->setEmitSignal(kCheatAdded);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::editCheat()
{
int idx = myCheatList->getSelected();
if(idx < 0)
return;
const CheatList& list = instance().cheat().list();
const string& name = list[idx]->name();
const string& code = list[idx]->code();
myCheatInput->show(); // Center input dialog over entire screen
myCheatInput->setText(name, 0);
myCheatInput->setText(code, 1);
myCheatInput->setTitle("");
myCheatInput->setFocus(1);
myCheatInput->setEmitSignal(kCheatEdited);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::removeCheat()
{
instance().cheat().remove(myCheatList->getSelected());
loadConfig(); // reload the cheat list
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::addOneShotCheat()
{
myCheatInput->show(); // Center input dialog over entire screen
myCheatInput->setText("One-shot cheat", 0);
myCheatInput->setText("", 1);
myCheatInput->setTitle("");
myCheatInput->setFocus(1);
myCheatInput->setEmitSignal(kOneShotCheatAdded);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatCodeDialog::handleCommand(CommandSender* sender, int cmd,
int data, int id)
{
switch(cmd)
{
case kOKCmd:
saveConfig();
close();
break;
case kCloseCmd:
close();
break;
case ListWidget::kDoubleClickedCmd:
editCheat();
break;
case kAddCheatCmd:
addCheat();
break;
case kEditCheatCmd:
editCheat();
break;
case kCheatAdded:
{
const string& name = myCheatInput->getResult(0);
const string& code = myCheatInput->getResult(1);
if(instance().cheat().isValidCode(code))
{
myCheatInput->close();
instance().cheat().add(name, code);
loadConfig(); // show changes onscreen
}
else
myCheatInput->setTitle("Invalid code");
break;
}
case kCheatEdited:
{
const string& name = myCheatInput->getResult(0);
const string& code = myCheatInput->getResult(1);
bool enable = myCheatList->getSelectedState();
int idx = myCheatList->getSelected();
if(instance().cheat().isValidCode(code))
{
myCheatInput->close();
instance().cheat().add(name, code, enable, idx);
loadConfig(); // show changes onscreen
}
else
myCheatInput->setTitle("Invalid code");
break;
}
case kRemCheatCmd:
removeCheat();
break;
case kAddOneShotCmd:
addOneShotCheat();
break;
case kOneShotCheatAdded:
{
const string& name = myCheatInput->getResult(0);
const string& code = myCheatInput->getResult(1);
if(instance().cheat().isValidCode(code))
{
myCheatInput->close();
instance().cheat().addOneShot(name, code);
}
else
myCheatInput->setTitle("Invalid code");
break;
}
default:
Dialog::handleCommand(sender, cmd, data, 0);
break;
}
}
+72
View File
@@ -0,0 +1,72 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CheatCodeDialog.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CHEAT_CODE_DIALOG_HXX
#define CHEAT_CODE_DIALOG_HXX
class DialogContainer;
class CommandSender;
class Widget;
class ButtonWidget;
class StaticTextWidget;
class CheckListWidget;
class EditTextWidget;
class InputTextDialog;
class OptionsDialog;
class OSystem;
#include "Dialog.hxx"
class CheatCodeDialog : public Dialog
{
public:
CheatCodeDialog(OSystem* osystem, DialogContainer* parent,
const GUI::Font& font);
~CheatCodeDialog();
protected:
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
void loadConfig();
void saveConfig();
private:
void addCheat();
void editCheat();
void removeCheat();
void addOneShotCheat();
private:
CheckListWidget* myCheatList;
InputTextDialog* myCheatInput;
ButtonWidget* myEditButton;
ButtonWidget* myRemoveButton;
enum {
kAddCheatCmd = 'CHTa',
kEditCheatCmd = 'CHTe',
kAddOneShotCmd = 'CHTo',
kCheatAdded = 'CHad',
kCheatEdited = 'CHed',
kOneShotCheatAdded = 'CHoa',
kRemCheatCmd = 'CHTr'
};
};
#endif
+371
View File
@@ -0,0 +1,371 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CheatManager.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <fstream>
#include <sstream>
#include "OSystem.hxx"
#include "Console.hxx"
#include "Cheat.hxx"
#include "Settings.hxx"
#include "CheetahCheat.hxx"
#include "BankRomCheat.hxx"
#include "RamCheat.hxx"
#include "CheatManager.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheatManager::CheatManager(OSystem* osystem)
: myOSystem(osystem),
myCurrentCheat(""),
myListIsDirty(false)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheatManager::~CheatManager()
{
saveCheatDatabase();
myCheatMap.clear();
clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Cheat* CheatManager::add(const string& name, const string& code,
bool enable, int idx)
{
Cheat* cheat = (Cheat*) createCheat(name, code);
if(!cheat)
return NULL;
// Delete duplicate entries
for(unsigned int i = 0; i < myCheatList.size(); i++)
{
if(myCheatList[i]->name() == name || myCheatList[i]->code() == code)
{
myCheatList.remove_at(i);
break;
}
}
// Add the cheat to the main cheat list
if(idx == -1)
myCheatList.push_back(cheat);
else
myCheatList.insert_at(idx, cheat);
// And enable/disable it (the cheat knows how to enable or disable itself)
if(enable)
cheat->enable();
else
cheat->disable();
return cheat;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::remove(int idx)
{
if((unsigned int)idx >= myCheatList.size())
return;
Cheat* c = myCheatList[idx];
// First remove it from the per-frame list
addPerFrame(c, false);
// Then remove it from the cheatlist entirely
myCheatList.remove_at(idx);
c->disable();
delete c;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::addPerFrame(Cheat* cheat, bool enable)
{
if(!cheat)
return;
// Make sure there are no duplicates
bool found = false;
unsigned int i;
for(i = 0; i < myPerFrameList.size(); i++)
{
if(myPerFrameList[i]->code() == cheat->code())
{
found = true;
break;
}
}
if(enable)
{
if(!found)
myPerFrameList.push_back(cheat);
}
else
{
if(found)
myPerFrameList.remove_at(i);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::addOneShot(const string& name, const string& code)
{
Cheat* cheat = (Cheat*) createCheat(name, code);
if(!cheat)
return;
// Evaluate this cheat once, and then immediately delete it
cheat->evaluate();
delete cheat;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Cheat* CheatManager::createCheat(const string& name, const string& code)
{
if(!isValidCode(code))
return NULL;
// Create new cheat based on string length
switch(code.size())
{
case 4:
return new RamCheat(myOSystem, name, code);
break;
case 6:
return new CheetahCheat(myOSystem, name, code);
break;
case 8:
return new BankRomCheat(myOSystem, name, code);
break;
default:
return NULL;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::parse(const string& cheats)
{
StringList s;
string::size_type lastPos = cheats.find_first_not_of(",", 0);
string::size_type pos = cheats.find_first_of(",", lastPos);
string cheat, name, code;
// Split string by comma, getting each cheat
while(string::npos != pos || string::npos != lastPos)
{
// Get the next cheat
cheat = cheats.substr(lastPos, pos - lastPos);
// Split cheat by colon, separating each part
string::size_type lastColonPos = cheat.find_first_not_of(":", 0);
string::size_type colonPos = cheat.find_first_of(":", lastColonPos);
while(string::npos != colonPos || string::npos != lastColonPos)
{
s.push_back(cheat.substr(lastColonPos, colonPos - lastColonPos));
lastColonPos = cheat.find_first_not_of(":", colonPos);
colonPos = cheat.find_first_of(":", lastColonPos);
}
// Account for variable number of items specified for cheat
switch(s.size())
{
case 1:
name = s[0];
code = name;
add(name, code, true);
break;
case 2:
name = s[0];
code = s[1];
add(name, code, true);
break;
case 3:
name = s[0];
code = s[1];
add(name, code, s[2] == "1");
break;
}
s.clear();
lastPos = cheats.find_first_not_of(",", pos);
pos = cheats.find_first_of(",", lastPos);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::enable(const string& code, bool enable)
{
for(unsigned int i = 0; i < myCheatList.size(); i++)
{
if(myCheatList[i]->code() == code)
{
if(enable)
myCheatList[i]->enable();
else
myCheatList[i]->disable();
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::loadCheatDatabase()
{
const string& cheatfile = myOSystem->cheatFile();
ifstream in(cheatfile.c_str(), ios::in);
if(!in)
return;
string line, md5, cheat;
string::size_type one, two, three, four;
// Loop reading cheats
while(getline(in, line))
{
if(line.length() == 0)
continue;
one = line.find("\"", 0);
two = line.find("\"", one + 1);
three = line.find("\"", two + 1);
four = line.find("\"", three + 1);
// Invalid line if it doesn't contain 4 quotes
if((one == string::npos) || (two == string::npos) ||
(three == string::npos) || (four == string::npos))
break;
// Otherwise get the ms5sum and associated cheats
md5 = line.substr(one + 1, two - one - 1);
cheat = line.substr(three + 1, four - three - 1);
myCheatMap.insert(make_pair(md5, cheat));
}
in.close();
myListIsDirty = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::saveCheatDatabase()
{
if(!myListIsDirty)
return;
const string& cheatfile = myOSystem->cheatFile();
ofstream out(cheatfile.c_str(), ios::out);
if(!out)
return;
CheatCodeMap::iterator iter;
for(iter = myCheatMap.begin(); iter != myCheatMap.end(); ++iter)
out << "\"" << iter->first << "\" "
<< "\"" << iter->second << "\""
<< endl;
out.close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::loadCheats(const string& md5sum)
{
clear();
myCurrentCheat = "";
// Set up any cheatcodes that was on the command line
// (and remove the key from the settings, so they won't get set again)
const string& cheats = myOSystem->settings().getString("cheat");
if(cheats != "")
myOSystem->settings().setValue("cheat", "");
CheatCodeMap::iterator iter = myCheatMap.find(md5sum);
if(iter == myCheatMap.end() && cheats == "")
return;
// Remember the cheats for this ROM
myCurrentCheat = iter->second;
// Parse the cheat list, constructing cheats and adding them to the manager
parse(iter->second + cheats);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::saveCheats(const string& md5sum)
{
ostringstream cheats;
for(unsigned int i = 0; i < myCheatList.size(); i++)
{
cheats << myCheatList[i]->name() << ":"
<< myCheatList[i]->code() << ":"
<< myCheatList[i]->enabled();
if(i+1 < myCheatList.size())
cheats << ",";
}
bool changed = cheats.str() != myCurrentCheat;
// Only update the list if absolutely necessary
if(changed)
{
CheatCodeMap::iterator iter = myCheatMap.find(md5sum);
// Erase old entry and add a new one only if it's changed
if(iter != myCheatMap.end())
myCheatMap.erase(iter);
// Add new entry only if there are any cheats defined
if(cheats.str() != "")
myCheatMap.insert(make_pair(md5sum, cheats.str()));
}
// Update the dirty flag
myListIsDirty = myListIsDirty || changed;
clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::clear()
{
// Don't delete the items from per-frame list, since it will be done in
// the following loop
myPerFrameList.clear();
for(unsigned int i = 0; i < myCheatList.size(); i++)
delete myCheatList[i];
myCheatList.clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheatManager::isValidCode(const string& code)
{
for(unsigned int i = 0; i < code.size(); i++)
if(!isxdigit(code[i]))
return false;
int length = code.length();
return (length == 4 || length == 6 || length == 8);
}
+171
View File
@@ -0,0 +1,171 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CheatManager.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CHEAT_MANAGER_HXX
#define CHEAT_MANAGER_HXX
#include <map>
class Cheat;
class OSystem;
#include "bspf.hxx"
#include "Array.hxx"
typedef Common::Array<Cheat*> CheatList;
typedef map<string,string> CheatCodeMap;
/**
This class provides an interface for performing all cheat operations
in Stella. It is accessible from the OSystem interface, and contains
the list of all cheats currently in use.
@author Stephen Anthony
@version $Id: CheatManager.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class CheatManager
{
public:
CheatManager(OSystem* osystem);
virtual ~CheatManager();
/**
Adds the specified cheat to an internal list.
@param name Name of the cheat (not absolutely required)
@param code The actual cheatcode (in hex)
@param enable Whether to enable this cheat right away
@param idx Index at which to insert the cheat
@return The cheat (if was created), else NULL.
*/
const Cheat* add(const string& name, const string& code,
bool enable = true, int idx = -1);
/**
Remove the cheat at 'idx' from the cheat list(s).
@param index Location in myCheatList of the cheat to remove
*/
void remove(int idx);
/**
Adds the specified cheat to the internal per-frame list.
This method doesn't create a new cheat; it just adds/removes
an already created cheat to the per-frame list.
@param cheat The actual cheat object
@param enable Add or remove the cheat to the per-frame list
*/
void addPerFrame(Cheat* cheat, bool enable);
/**
Creates and enables a one-shot cheat. One-shot cheats are the
same as normal cheats, except they are only enabled once, and
they're not saved at all.
@param name Name of the cheat (not absolutely required)
@param code The actual cheatcode (in hex)
*/
void addOneShot(const string& name, const string& code);
/**
Enable/disabled the cheat specified by the given code.
@param code The actual cheatcode to search for
@param enable Enable/disable the cheat
*/
void enable(const string& code, bool enable);
/**
Returns the game cheatlist.
*/
const CheatList& list() { return myCheatList; }
/**
Returns the per-frame cheatlist (needed to evaluate cheats each frame)
*/
const CheatList& perFrame() { return myPerFrameList; }
/**
Load all cheats (for all ROMs) from disk to internal database.
*/
void loadCheatDatabase();
/**
Save all cheats (for all ROMs) in internal database to disk.
*/
void saveCheatDatabase();
/**
Load cheats for ROM with given MD5sum to cheatlist(s).
*/
void loadCheats(const string& md5sum);
/**
Saves cheats for ROM with given MD5sum to cheat map.
*/
void saveCheats(const string& md5sum);
/**
Checks if a code is valid.
*/
bool isValidCode(const string& code);
private:
/**
Create a cheat defined by the given code.
@param name Name of the cheat (not absolutely required)
@param code The actual cheatcode (in hex)
@return The cheat (if was created), else NULL.
*/
const Cheat* createCheat(const string& name, const string& code);
/**
Parses a list of cheats and adds/enables each one.
@param cheats Comma-separated list of cheats (without any names)
*/
void parse(const string& cheats);
/**
Clear all per-ROM cheats lists.
*/
void clear();
private:
OSystem* myOSystem;
CheatList myCheatList;
CheatList myPerFrameList;
CheatCodeMap myCheatMap;
string myCheatFile;
// This is set each time a new cheat/ROM is loaded, for later
// comparison to see if the cheatcode list has actually been modified
string myCurrentCheat;
// Indicates that the list has been modified, and should be saved to disk
bool myListIsDirty;
};
#endif
+69
View File
@@ -0,0 +1,69 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CheetahCheat.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Console.hxx"
#include "Cart.hxx"
#include "OSystem.hxx"
#include "CheetahCheat.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheetahCheat::CheetahCheat(OSystem* os, const string& name, const string& code)
: Cheat(os, name, code)
{
address = 0xf000 + unhex(code.substr(0, 3));
value = unhex(code.substr(3, 2));
count = unhex(code.substr(5, 1)) + 1;
// Back up original data; we need this if the cheat is ever disabled
for(int i = 0; i < count; ++i)
savedRom[i] = myOSystem->console().cartridge().peek(address + i);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheetahCheat::~CheetahCheat()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheetahCheat::enable()
{
evaluate();
return myEnabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheetahCheat::disable()
{
for(int i = 0; i < count; ++i)
myOSystem->console().cartridge().patch(address + i, savedRom[i]);
return myEnabled = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheetahCheat::evaluate()
{
if(!myEnabled)
{
for(int i = 0; i < count; ++i)
myOSystem->console().cartridge().patch(address + i, value);
myEnabled = true;
}
}
+43
View File
@@ -0,0 +1,43 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CheetahCheat.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CHEETAH_CHEAT_HXX
#define CHEETAH_CHEAT_HXX
#include "Cheat.hxx"
class CheetahCheat : public Cheat
{
public:
CheetahCheat(OSystem* os, const string& name, const string& code);
~CheetahCheat();
virtual bool enable();
virtual bool disable();
virtual void evaluate();
private:
uInt8 savedRom[16];
uInt16 address;
uInt8 value;
uInt8 count;
};
#endif
+66
View File
@@ -0,0 +1,66 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RamCheat.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Console.hxx"
#include "System.hxx"
#include "OSystem.hxx"
#include "CheatManager.hxx"
#include "RamCheat.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RamCheat::RamCheat(OSystem* os, const string& name, const string& code)
: Cheat(os, name, code)
{
address = (uInt16) unhex(myCode.substr(0, 2));
value = (uInt8) unhex(myCode.substr(2, 2));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RamCheat::~RamCheat()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RamCheat::enable()
{
if(!myEnabled)
{
myEnabled = true;
myOSystem->cheat().addPerFrame(this, myEnabled);
}
return myEnabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RamCheat::disable()
{
if(myEnabled)
{
myEnabled = false;
myOSystem->cheat().addPerFrame(this, myEnabled);
}
return myEnabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RamCheat::evaluate()
{
myOSystem->console().system().poke(address, value);
}
+41
View File
@@ -0,0 +1,41 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RamCheat.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef RAM_CHEAT_HXX
#define RAM_CHEAT_HXX
#include "Cheat.hxx"
class RamCheat : public Cheat
{
public:
RamCheat(OSystem* os, const string& name, const string& code);
virtual ~RamCheat();
virtual bool enable();
virtual bool disable();
virtual void evaluate();
private:
uInt16 address;
uInt8 value;
};
#endif
+14
View File
@@ -0,0 +1,14 @@
MODULE := src/cheat
MODULE_OBJS := \
src/cheat/CheatCodeDialog.o \
src/cheat/CheatManager.o \
src/cheat/CheetahCheat.o \
src/cheat/BankRomCheat.o \
src/cheat/RamCheat.o
MODULE_DIRS += \
src/cheat
# Include common rules
include $(srcdir)/common.rules
@@ -8,13 +8,13 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2011 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Array.hxx 2232 2011-05-24 16:04:48Z stephena $
// $Id: Array.hxx 2838 2014-01-17 23:34:03Z stephena $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@@ -33,8 +33,8 @@ template <class T>
class Array
{
protected:
int _capacity;
int _size;
uInt32 _capacity;
uInt32 _size;
T *_data;
public:
@@ -48,7 +48,7 @@ class Array
_size = array._size;
_capacity = _size + 128;
_data = new T[_capacity];
for(int i = 0; i < _size; i++)
for(uInt32 i = 0; i < _size; i++)
_data[i] = array._data[i];
}
@@ -58,6 +58,13 @@ class Array
delete [] _data;
}
void reserve(uInt32 capacity)
{
if(capacity <= _capacity)
return;
ensureCapacity(capacity - 128);
}
void push_back(const T& element)
{
ensureCapacity(_size + 1);
@@ -67,20 +74,11 @@ class Array
void push_back(const Array<T>& array)
{
ensureCapacity(_size + array._size);
for(int i = 0; i < array._size; i++)
for(uInt32 i = 0; i < array._size; i++)
_data[_size++] = array._data[i];
}
void push_back_unique(const T& element)
{
if(!contains(element))
{
ensureCapacity(_size + 1);
_data[_size++] = element;
}
}
void insert_at(int idx, const T& element)
void insert_at(uInt32 idx, const T& element)
{
assert(idx >= 0 && idx <= _size);
ensureCapacity(_size + 1);
@@ -89,32 +87,30 @@ class Array
// usually isn't correct (specifically, for any class which has a non-default
// copy behaviour. E.g. the String class uses a refCounter which has to be
// updated whenever a String is copied.
for(int i = _size; i > idx; i--)
for(uInt32 i = _size; i > idx; i--)
_data[i] = _data[i-1];
_data[idx] = element;
_size++;
}
T remove_at(int idx)
T remove_at(uInt32 idx)
{
assert(idx >= 0 && idx < _size);
T tmp = _data[idx];
for(int i = idx; i < _size - 1; i++)
for(uInt32 i = idx; i < _size - 1; i++)
_data[i] = _data[i+1];
_size--;
return tmp;
}
// TODO: insert, remove, ...
T& operator [](int idx)
T& operator [](uInt32 idx)
{
assert(idx >= 0 && idx < _size);
return _data[idx];
}
const T& operator [](int idx) const
const T& operator [](uInt32 idx) const
{
assert(idx >= 0 && idx < _size);
return _data[idx];
@@ -127,23 +123,27 @@ class Array
_size = array._size;
_capacity = _size + 128;
_data = new T[_capacity];
for(int i = 0; i < _size; i++)
for(uInt32 i = 0; i < _size; i++)
_data[i] = array._data[i];
return *this;
}
unsigned int size() const { return _size; }
uInt32 size() const { return _size; }
uInt32 capacity() const { return _capacity; }
void clear()
void clear(bool fullerase = true)
{
if(_data)
if(fullerase)
{
delete [] _data;
_data = 0;
if(_data)
{
delete [] _data;
_data = 0;
}
_capacity = 0;
}
_size = 0;
_capacity = 0;
}
bool isEmpty() const
@@ -171,17 +171,8 @@ class Array
return _data + _size;
}
bool contains(const T &key) const
{
for (const_iterator i = begin(); i != end(); ++i) {
if (*i == key)
return true;
}
return false;
}
protected:
void ensureCapacity(int new_len)
void ensureCapacity(uInt32 new_len)
{
if (new_len <= _capacity)
return;
@@ -193,7 +184,7 @@ class Array
if (old_data)
{
// Copy old data
for (int i = 0; i < _size; i++)
for (uInt32 i = 0; i < _size; i++)
_data[i] = old_data[i];
delete [] old_data;
}
+114
View File
@@ -0,0 +1,114 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Base.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Base.hxx"
namespace Common {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Base::setHexUppercase(bool enable)
{
if(enable)
{
myHexflags |= std::ios_base::uppercase;
myFmt = Base::myUpperFmt;
}
else
{
myHexflags &= ~std::ios_base::uppercase;
myFmt = Base::myLowerFmt;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Base::toString(int value, Common::Base::Format outputBase)
{
static char vToS_buf[32];
if(outputBase == Base::F_DEFAULT)
outputBase = myDefaultBase;
switch(outputBase)
{
case Base::F_2: // base 2: 8 or 16 bits (depending on value)
case Base::F_2_8: // base 2: 1 byte (8 bits) wide
case Base::F_2_16: // base 2: 2 bytes (16 bits) wide
{
int places = (outputBase == Base::F_2_8 ||
(outputBase == Base::F_2 && value < 0x100)) ? 8 : 16;
vToS_buf[places] = '\0';
int bit = 1;
while(--places >= 0) {
if(value & bit) vToS_buf[places] = '1';
else vToS_buf[places] = '0';
bit <<= 1;
}
break;
}
case Base::F_10: // base 10: 3 or 5 bytes (depending on value)
if(value < 0x100)
BSPF_snprintf(vToS_buf, 4, "%3d", value);
else
BSPF_snprintf(vToS_buf, 6, "%5d", value);
break;
case Base::F_16_1: // base 16: 1 byte wide
BSPF_snprintf(vToS_buf, 2, myFmt[0], value);
break;
case Base::F_16_2: // base 16: 2 bytes wide
BSPF_snprintf(vToS_buf, 3, myFmt[1], value);
break;
case Base::F_16_4: // base 16: 4 bytes wide
BSPF_snprintf(vToS_buf, 5, myFmt[2], value);
break;
case Base::F_16_8: // base 16: 8 bytes wide
BSPF_snprintf(vToS_buf, 9, myFmt[3], value);
break;
case Base::F_16: // base 16: 2, 4, 8 bytes (depending on value)
default:
if(value < 0x100)
BSPF_snprintf(vToS_buf, 3, myFmt[1], value);
else if(value < 0x10000)
BSPF_snprintf(vToS_buf, 5, myFmt[2], value);
else
BSPF_snprintf(vToS_buf, 9, myFmt[3], value);
break;
}
return string(vToS_buf);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Base::Format Base::myDefaultBase = Base::F_16;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::ios_base::fmtflags Base::myHexflags = std::ios_base::hex;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const char* Base::myLowerFmt[4] = {
"%1x", "%02x", "%04x", "%08x"
};
const char* Base::myUpperFmt[4] = {
"%1X", "%02X", "%04X", "%08X"
};
const char** Base::myFmt = Base::myLowerFmt;
} // Namespace Common
+102
View File
@@ -0,0 +1,102 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Base.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef BASE_HXX
#define BASE_HXX
#include <iostream>
#include <iomanip>
#include "bspf.hxx"
namespace Common {
/**
This class implements several functions for converting integer data
into strings in multiple bases, with different formats (# of characters,
upper/lower-case, etc).
@author Stephen Anthony
*/
class Base
{
public:
// The base to use for conversion from integers to strings
// Note that the actual number of places will be determined by
// the magnitude of the value itself in the general case
enum Format {
F_16, // base 16: 2, 4, 8 bytes (depending on value)
F_16_1, // base 16: 1 byte wide
F_16_2, // base 16: 2 bytes wide
F_16_4, // base 16: 4 bytes wide
F_16_8, // base 16: 8 bytes wide
F_10, // base 10: 3 or 5 bytes (depending on value)
F_2, // base 2: 8 or 16 bits (depending on value)
F_2_8, // base 2: 1 byte (8 bits) wide
F_2_16, // base 2: 2 bytes (16 bits) wide
F_DEFAULT
};
public:
/** Get/set the number base when parsing numeric values */
static void setFormat(Base::Format base) { myDefaultBase = base; }
static Base::Format format() { return myDefaultBase; }
/** Get/set HEX output to be upper/lower case */
static void setHexUppercase(bool enable);
static bool hexUppercase() { return myHexflags & std::ios_base::uppercase; }
/** Output HEX digits in 1/2/4 byte format */
static inline std::ostream& HEX2(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(2) << std::setfill('0');
}
static inline std::ostream& HEX4(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(4) << std::setfill('0');
}
static inline std::ostream& HEX8(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(8) << std::setfill('0');
}
/** Convert integer to a string in the given base format */
static string toString(int value,
Common::Base::Format outputBase = Common::Base::F_DEFAULT);
private: // Make sure this class is never instantiated
Base() { }
private:
// Default format to use when none is specified
static Format myDefaultBase;
// Upper or lower case for HEX digits
static std::ios_base::fmtflags myHexflags;
// Format specifiers to use for sprintf (eventually we may convert
// to C++ streams
static const char* myLowerFmt[4];
static const char* myUpperFmt[4];
static const char** myFmt;
};
} // Namespace Common
#endif
+372
View File
@@ -0,0 +1,372 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FBSurfaceGL.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifdef DISPLAY_OPENGL
#include "Font.hxx"
#include "FrameBufferGL.hxx"
#include "FBSurfaceGL.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceGL::FBSurfaceGL(FrameBufferGL& buffer, uInt32 width, uInt32 height)
: myFB(buffer),
myGL(myFB.p_gl),
myTexture(NULL),
myTexID(0),
myVBOID(0),
myImageX(0),
myImageY(0),
myImageW(width),
myImageH(height)
{
// Fill buffer struct with valid data
myTexWidth = FrameBufferGL::power_of_two(myImageW);
myTexHeight = FrameBufferGL::power_of_two(myImageH);
myTexCoordW = (GLfloat) myImageW / myTexWidth;
myTexCoordH = (GLfloat) myImageH / myTexHeight;
// Create a surface in the same format as the parent GL class
const SDL_PixelFormat& pf = myFB.myPixelFormat;
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, myTexWidth, myTexHeight,
pf.BitsPerPixel, pf.Rmask, pf.Gmask, pf.Bmask, pf.Amask);
myPitch = myTexture->pitch / pf.BytesPerPixel;
// Associate the SDL surface with a GL texture object
updateCoords();
reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceGL::~FBSurfaceGL()
{
if(myTexture)
SDL_FreeSurface(myTexture);
free();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color)
{
uInt32* buffer = (uInt32*) myTexture->pixels + y * myPitch + x;
while(x++ <= x2)
*buffer++ = (uInt32) myFB.myDefPalette[color];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::vLine(uInt32 x, uInt32 y, uInt32 y2, uInt32 color)
{
uInt32* buffer = (uInt32*) myTexture->pixels + y * myPitch + x;
while(y++ <= y2)
{
*buffer = (uInt32) myFB.myDefPalette[color];
buffer += myPitch;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color)
{
// Fill the rectangle
SDL_Rect tmp;
tmp.x = x;
tmp.y = y;
tmp.w = w;
tmp.h = h;
SDL_FillRect(myTexture, &tmp, myFB.myDefPalette[color]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::drawChar(const GUI::Font& font, uInt8 chr,
uInt32 tx, uInt32 ty, uInt32 color)
{
const FontDesc& desc = font.desc();
// If this character is not included in the font, use the default char.
if(chr < desc.firstchar || chr >= desc.firstchar + desc.size)
{
if (chr == ' ') return;
chr = desc.defaultchar;
}
chr -= desc.firstchar;
// Get the bounding box of the character
int bbw, bbh, bbx, bby;
if(!desc.bbx)
{
bbw = desc.fbbw;
bbh = desc.fbbh;
bbx = desc.fbbx;
bby = desc.fbby;
}
else
{
bbw = desc.bbx[chr].w;
bbh = desc.bbx[chr].h;
bbx = desc.bbx[chr].x;
bby = desc.bbx[chr].y;
}
const uInt16* tmp = desc.bits + (desc.offset ? desc.offset[chr] : (chr * desc.fbbh));
uInt32* buffer = (uInt32*) myTexture->pixels +
(ty + desc.ascent - bby - bbh) * myPitch +
tx + bbx;
for(int y = 0; y < bbh; y++)
{
const uInt16 ptr = *tmp++;
uInt16 mask = 0x8000;
for(int x = 0; x < bbw; x++, mask >>= 1)
if(ptr & mask)
buffer[x] = (uInt32) myFB.myDefPalette[color];
buffer += myPitch;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::drawBitmap(uInt32* bitmap, uInt32 tx, uInt32 ty,
uInt32 color, uInt32 h)
{
uInt32* buffer = (uInt32*) myTexture->pixels + ty * myPitch + tx;
for(uInt32 y = 0; y < h; ++y)
{
uInt32 mask = 0xF0000000;
for(uInt32 x = 0; x < 8; ++x, mask >>= 4)
if(bitmap[y] & mask)
buffer[x] = (uInt32) myFB.myDefPalette[color];
buffer += myPitch;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::drawPixels(uInt32* data, uInt32 tx, uInt32 ty, uInt32 numpixels)
{
uInt32* buffer = (uInt32*) myTexture->pixels + ty * myPitch + tx;
for(uInt32 i = 0; i < numpixels; ++i)
*buffer++ = (uInt32) data[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::drawSurface(const FBSurface* surface, uInt32 tx, uInt32 ty)
{
const FBSurfaceGL* s = (const FBSurfaceGL*) surface;
SDL_Rect dstrect;
dstrect.x = tx;
dstrect.y = ty;
SDL_Rect srcrect;
srcrect.x = 0;
srcrect.y = 0;
srcrect.w = s->myImageW;
srcrect.h = s->myImageH;
SDL_BlitSurface(s->myTexture, &srcrect, myTexture, &dstrect);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
// OpenGL mode doesn't make use of dirty rectangles
// It's faster to just update the entire surface
mySurfaceIsDirty = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::getPos(uInt32& x, uInt32& y) const
{
x = myImageX;
y = myImageY;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::setPos(uInt32 x, uInt32 y)
{
if(myImageX != x || myImageY != y)
{
myImageX = x;
myImageY = y;
updateCoords();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::setWidth(uInt32 w)
{
// This method can't be used with 'scaled' surface (aka TIA surfaces)
// That shouldn't really matter, though, as all the UI stuff isn't scaled,
// and it's the only thing that uses it
if(myImageW != w)
{
myImageW = BSPF_min(w, (uInt32)myTexWidth);
myTexCoordW = (GLfloat) myImageW / myTexWidth;
updateCoords();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::setHeight(uInt32 h)
{
// This method can't be used with 'scaled' surface (aka TIA surfaces)
// That shouldn't really matter, though, as all the UI stuff isn't scaled,
// and it's the only thing that uses it
if(myImageH != h)
{
myImageH = BSPF_min(h, (uInt32)myTexHeight);
myTexCoordH = (GLfloat) myImageH / myTexHeight;
updateCoords();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::translateCoords(Int32& x, Int32& y) const
{
x -= myImageX;
y -= myImageY;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::update()
{
if(mySurfaceIsDirty)
{
// Texturemap complete texture to surface so we have free scaling
// and antialiasing
myGL.ActiveTexture(GL_TEXTURE0);
myGL.BindTexture(GL_TEXTURE_2D, myTexID);
myGL.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
myGL.PixelStorei(GL_UNPACK_ROW_LENGTH, myPitch);
myGL.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myImageW, myImageH,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
myTexture->pixels);
myGL.EnableClientState(GL_VERTEX_ARRAY);
myGL.EnableClientState(GL_TEXTURE_COORD_ARRAY);
if(myFB.myVBOAvailable)
{
myGL.BindBuffer(GL_ARRAY_BUFFER, myVBOID);
myGL.VertexPointer(2, GL_FLOAT, 0, (const GLvoid*)0);
myGL.TexCoordPointer(2, GL_FLOAT, 0, (const GLvoid*)(8*sizeof(GLfloat)));
}
else
{
myGL.VertexPointer(2, GL_FLOAT, 0, myCoord);
myGL.TexCoordPointer(2, GL_FLOAT, 0, myCoord+8);
}
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
myGL.DisableClientState(GL_VERTEX_ARRAY);
myGL.DisableClientState(GL_TEXTURE_COORD_ARRAY);
mySurfaceIsDirty = false;
// Let postFrameUpdate() know that a change has been made
myFB.myDirtyFlag = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::free()
{
myGL.DeleteTextures(1, &myTexID);
if(myFB.myVBOAvailable)
myGL.DeleteBuffers(1, &myVBOID);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::reload()
{
// This does a 'soft' reset of the surface
// It seems that on some system (notably, OSX), creating a new SDL window
// destroys the GL context, requiring a reload of all textures
// However, destroying the entire FBSurfaceGL object is wasteful, since
// it will also regenerate SDL software surfaces (which are not required
// to be regenerated)
// Basically, all that needs to be done is to re-call glTexImage2D with a
// new texture ID, so that's what we do here
myGL.ActiveTexture(GL_TEXTURE0);
myGL.Enable(GL_TEXTURE_2D);
myGL.GenTextures(1, &myTexID);
myGL.BindTexture(GL_TEXTURE_2D, myTexID);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Create the texture in the most optimal format
myGL.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
myGL.PixelStorei(GL_UNPACK_ROW_LENGTH, myPitch);
myGL.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, myTexWidth, myTexHeight, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
myTexture->pixels);
// Cache vertex and texture coordinates using vertex buffer object
if(myFB.myVBOAvailable)
{
myGL.GenBuffers(1, &myVBOID);
myGL.BindBuffer(GL_ARRAY_BUFFER, myVBOID);
myGL.BufferData(GL_ARRAY_BUFFER, 16*sizeof(GLfloat), myCoord, GL_STATIC_DRAW);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::updateCoords()
{
// Vertex coordinates for texture
// Upper left (x,y)
myCoord[0] = (GLfloat)myImageX;
myCoord[1] = (GLfloat)myImageY;
// Upper right (x+w,y)
myCoord[2] = (GLfloat)(myImageX + myImageW);
myCoord[3] = (GLfloat)myImageY;
// Lower left (x,y+h)
myCoord[4] = (GLfloat)myImageX;
myCoord[5] = (GLfloat)(myImageY + myImageH);
// Lower right (x+w,y+h)
myCoord[6] = (GLfloat)(myImageX + myImageW);
myCoord[7] = (GLfloat)(myImageY + myImageH);
// Texture coordinates for texture
// Upper left (x,y)
myCoord[8] = 0.0f;
myCoord[9] = 0.0f;
// Upper right (x+w,y)
myCoord[10] = myTexCoordW;
myCoord[11] = 0.0f;
// Lower left (x,y+h)
myCoord[12] = 0.0f;
myCoord[13] = myTexCoordH;
// Lower right (x+w,y+h)
myCoord[14] = myTexCoordW;
myCoord[15] = myTexCoordH;
// Cache vertex and texture coordinates using vertex buffer object
if(myFB.myVBOAvailable)
{
myGL.BindBuffer(GL_ARRAY_BUFFER, myVBOID);
myGL.BufferData(GL_ARRAY_BUFFER, 16*sizeof(GLfloat), myCoord, GL_STATIC_DRAW);
}
}
#endif
+84
View File
@@ -0,0 +1,84 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FBSurfaceGL.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef FB_SURFACE_GL_HXX
#define FB_SURFACE_GL_HXX
#ifdef DISPLAY_OPENGL
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferGL.hxx"
/**
A surface suitable for OpenGL rendering mode, used for various UI dialogs.
This class extends FrameBuffer::FBSurface.
@author Stephen Anthony
*/
class FBSurfaceGL : public FBSurface
{
friend class FrameBufferGL;
public:
FBSurfaceGL(FrameBufferGL& buffer, uInt32 width, uInt32 height);
virtual ~FBSurfaceGL();
// Normal surfaces need all drawing primitives
void hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color);
void vLine(uInt32 x, uInt32 y, uInt32 y2, uInt32 color);
void fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color);
void drawChar(const GUI::Font& font, uInt8 c, uInt32 x, uInt32 y, uInt32 color);
void drawBitmap(uInt32* bitmap, uInt32 x, uInt32 y, uInt32 color, uInt32 h = 8);
void drawPixels(uInt32* data, uInt32 x, uInt32 y, uInt32 numpixels);
void drawSurface(const FBSurface* surface, uInt32 x, uInt32 y);
void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
void getPos(uInt32& x, uInt32& y) const;
void setPos(uInt32 x, uInt32 y);
uInt32 getWidth() const { return myImageW; }
uInt32 getHeight() const { return myImageH; }
void setWidth(uInt32 w);
void setHeight(uInt32 h);
void translateCoords(Int32& x, Int32& y) const;
void update();
void free();
void reload();
private:
void updateCoords();
private:
FrameBufferGL& myFB;
const FrameBufferGL::GLpointers& myGL;
SDL_Surface* myTexture;
GLuint myTexID, myVBOID;
GLsizei myTexWidth;
GLsizei myTexHeight;
GLuint myImageX, myImageY, myImageW, myImageH;
GLfloat myTexCoordW, myTexCoordH;
GLfloat myCoord[16];
bool mySurfaceIsDirty;
uInt32 myPitch;
};
#endif // DISPLAY_OPENGL
#endif
+395
View File
@@ -0,0 +1,395 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FBSurfaceTIA.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifdef DISPLAY_OPENGL
#include <cmath>
#include "Font.hxx"
#include "FrameBufferGL.hxx"
#include "TIA.hxx"
#include "NTSCFilter.hxx"
#include "FBSurfaceTIA.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceTIA::FBSurfaceTIA(FrameBufferGL& buffer)
: myFB(buffer),
myGL(myFB.p_gl),
myTexture(NULL),
myVBOID(0),
myBaseW(0),
myBaseH(0),
myScanlinesEnabled(false),
myScanlineIntensityI(50),
myScanlineIntensityF(0.5)
{
myTexID[0] = myTexID[1] = 0;
// Texture width is set to contain all possible sizes for a TIA image,
// including Blargg filtering
myTexWidth = FrameBufferGL::power_of_two(ATARI_NTSC_OUT_WIDTH(160));
myTexHeight = FrameBufferGL::power_of_two(320);
// Create a surface in the same format as the parent GL class
const SDL_PixelFormat& pf = myFB.myPixelFormat;
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, myTexWidth, myTexHeight,
pf.BitsPerPixel, pf.Rmask, pf.Gmask, pf.Bmask, pf.Amask);
myPitch = myTexture->pitch / pf.BytesPerPixel;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceTIA::~FBSurfaceTIA()
{
if(myTexture)
SDL_FreeSurface(myTexture);
free();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::getPos(uInt32& x, uInt32& y) const
{
x = myImageX;
y = myImageY;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::translateCoords(Int32& x, Int32& y) const
{
x -= myImageX;
y -= myImageY;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::update()
{
// Copy the mediasource framebuffer to the RGB texture
// In OpenGL mode, it's faster to just assume that the screen is dirty
// and always do an update
uInt8* currentFrame = myTIA->currentFrameBuffer();
uInt8* previousFrame = myTIA->previousFrameBuffer();
uInt32 width = myTIA->width();
uInt32 height = myTIA->height();
uInt32* buffer = (uInt32*) myTexture->pixels;
// TODO - Eventually 'phosphor' won't be a separate mode, and will become
// a post-processing filter by blending several frames.
switch(myFB.myFilterType)
{
case FrameBufferGL::kNormal:
{
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
buffer[pos++] = (uInt32) myFB.myDefPalette[currentFrame[bufofsY + x]];
bufofsY += width;
screenofsY += myPitch;
}
break;
}
case FrameBufferGL::kPhosphor:
{
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
buffer[pos++] = (uInt32)
myFB.myAvgPalette[currentFrame[bufofs]][previousFrame[bufofs]];
}
bufofsY += width;
screenofsY += myPitch;
}
break;
}
case FrameBufferGL::kBlarggNormal:
{
myFB.myNTSCFilter.blit_single(currentFrame, width, height,
buffer, myTexture->pitch);
break;
}
case FrameBufferGL::kBlarggPhosphor:
{
myFB.myNTSCFilter.blit_double(currentFrame, previousFrame, width, height,
buffer, myTexture->pitch);
break;
}
}
myGL.EnableClientState(GL_VERTEX_ARRAY);
myGL.EnableClientState(GL_TEXTURE_COORD_ARRAY);
// Update TIA image (texture 0), then blend scanlines (texture 1)
myGL.ActiveTexture(GL_TEXTURE0);
myGL.BindTexture(GL_TEXTURE_2D, myTexID[0]);
myGL.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
myGL.PixelStorei(GL_UNPACK_ROW_LENGTH, myPitch);
myGL.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myBaseW, myBaseH,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
myTexture->pixels);
if(myFB.myVBOAvailable)
{
myGL.BindBuffer(GL_ARRAY_BUFFER, myVBOID);
myGL.VertexPointer(2, GL_FLOAT, 0, (const GLvoid*)0);
myGL.TexCoordPointer(2, GL_FLOAT, 0, (const GLvoid*)(8*sizeof(GLfloat)));
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if(myScanlinesEnabled)
{
myGL.Enable(GL_BLEND);
myGL.Color4f(1.0f, 1.0f, 1.0f, myScanlineIntensityF);
myGL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
myGL.BindTexture(GL_TEXTURE_2D, myTexID[1]);
myGL.VertexPointer(2, GL_FLOAT, 0, (const GLvoid*)(16*sizeof(GLfloat)));
myGL.TexCoordPointer(2, GL_FLOAT, 0, (const GLvoid*)(24*sizeof(GLfloat)));
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
myGL.Disable(GL_BLEND);
}
}
else
{
myGL.VertexPointer(2, GL_FLOAT, 0, myCoord);
myGL.TexCoordPointer(2, GL_FLOAT, 0, myCoord+8);
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if(myScanlinesEnabled)
{
myGL.Enable(GL_BLEND);
myGL.Color4f(1.0f, 1.0f, 1.0f, myScanlineIntensityF);
myGL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
myGL.BindTexture(GL_TEXTURE_2D, myTexID[1]);
myGL.VertexPointer(2, GL_FLOAT, 0, myCoord+16);
myGL.TexCoordPointer(2, GL_FLOAT, 0, myCoord+24);
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
myGL.Disable(GL_BLEND);
}
}
myGL.DisableClientState(GL_VERTEX_ARRAY);
myGL.DisableClientState(GL_TEXTURE_COORD_ARRAY);
// Let postFrameUpdate() know that a change has been made
myFB.myDirtyFlag = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::invalidate()
{
SDL_FillRect(myTexture, NULL, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::free()
{
myGL.DeleteTextures(2, myTexID);
if(myFB.myVBOAvailable)
myGL.DeleteBuffers(1, &myVBOID);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::reload()
{
// This does a 'soft' reset of the surface
// It seems that on some system (notably, OSX), creating a new SDL window
// destroys the GL context, requiring a reload of all textures
// However, destroying the entire FBSurfaceGL object is wasteful, since
// it will also regenerate SDL software surfaces (which are not required
// to be regenerated)
// Basically, all that needs to be done is to re-call glTexImage2D with a
// new texture ID, so that's what we do here
myGL.ActiveTexture(GL_TEXTURE0);
myGL.Enable(GL_TEXTURE_2D);
// TIA surfaces also use a scanline texture
myGL.GenTextures(2, myTexID);
// Base texture (@ index 0)
myGL.BindTexture(GL_TEXTURE_2D, myTexID[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myTexFilter[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myTexFilter[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Create the texture in the most optimal format
myGL.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
myGL.PixelStorei(GL_UNPACK_ROW_LENGTH, myPitch);
myGL.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, myTexWidth, myTexHeight, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
myTexture->pixels);
// Scanline texture (@ index 1)
myGL.BindTexture(GL_TEXTURE_2D, myTexID[1]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myTexFilter[1]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myTexFilter[1]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
static uInt32 const scanline[2] = { 0x00000000, 0xff000000 };
myGL.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
myGL.PixelStorei(GL_UNPACK_ROW_LENGTH, 1);
myGL.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 2, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
scanline);
// Cache vertex and texture coordinates using vertex buffer object
if(myFB.myVBOAvailable)
{
myGL.GenBuffers(1, &myVBOID);
myGL.BindBuffer(GL_ARRAY_BUFFER, myVBOID);
myGL.BufferData(GL_ARRAY_BUFFER, 32*sizeof(GLfloat), myCoord, GL_STATIC_DRAW);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::setScanIntensity(uInt32 intensity)
{
myScanlineIntensityI = (GLuint)intensity;
myScanlineIntensityF = (GLfloat)intensity / 100;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::setTexInterpolation(bool enable)
{
myTexFilter[0] = enable ? GL_LINEAR : GL_NEAREST;
myGL.BindTexture(GL_TEXTURE_2D, myTexID[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myTexFilter[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myTexFilter[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::setScanInterpolation(bool enable)
{
myTexFilter[1] = enable ? GL_LINEAR : GL_NEAREST;
myGL.BindTexture(GL_TEXTURE_2D, myTexID[1]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myTexFilter[1]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myTexFilter[1]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::updateCoords(uInt32 baseH,
uInt32 imgX, uInt32 imgY, uInt32 imgW, uInt32 imgH)
{
myBaseH = baseH;
myImageX = imgX; myImageY = imgY;
myImageW = imgW; myImageH = imgH;
updateCoords();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::updateCoords()
{
// Normal TIA rendering and TV effects use different widths
// We use the same buffer, and only pick the width we need
myBaseW = myFB.ntscEnabled() ? ATARI_NTSC_OUT_WIDTH(160) : 160;
myTexCoordW = (GLfloat) myBaseW / myTexWidth;
myTexCoordH = (GLfloat) myBaseH / myTexHeight;
// Vertex coordinates for texture 0 (main texture)
// Upper left (x,y)
myCoord[0] = (GLfloat)myImageX;
myCoord[1] = (GLfloat)myImageY;
// Upper right (x+w,y)
myCoord[2] = (GLfloat)(myImageX + myImageW);
myCoord[3] = (GLfloat)myImageY;
// Lower left (x,y+h)
myCoord[4] = (GLfloat)myImageX;
myCoord[5] = (GLfloat)(myImageY + myImageH);
// Lower right (x+w,y+h)
myCoord[6] = (GLfloat)(myImageX + myImageW);
myCoord[7] = (GLfloat)(myImageY + myImageH);
// Texture coordinates for texture 0 (main texture)
// Upper left (x,y)
myCoord[8] = 0.0f;
myCoord[9] = 0.0f;
// Upper right (x+w,y)
myCoord[10] = myTexCoordW;
myCoord[11] = 0.0f;
// Lower left (x,y+h)
myCoord[12] = 0.0f;
myCoord[13] = myTexCoordH;
// Lower right (x+w,y+h)
myCoord[14] = myTexCoordW;
myCoord[15] = myTexCoordH;
// Vertex coordinates for texture 1 (scanline texture)
// Upper left (x,y)
myCoord[16] = (GLfloat)myImageX;
myCoord[17] = (GLfloat)myImageY;
// Upper right (x+w,y)
myCoord[18] = (GLfloat)(myImageX + myImageW);
myCoord[19] = (GLfloat)myImageY;
// Lower left (x,y+h)
myCoord[20] = (GLfloat)myImageX;
myCoord[21] = (GLfloat)(myImageY + myImageH);
// Lower right (x+w,y+h)
myCoord[22] = (GLfloat)(myImageX + myImageW);
myCoord[23] = (GLfloat)(myImageY + myImageH);
// Texture coordinates for texture 1 (scanline texture)
// Upper left (x,y)
myCoord[24] = 0.0f;
myCoord[25] = 0.0f;
// Upper right (x+w,y)
myCoord[26] = 1.0f;
myCoord[27] = 0.0f;
// Scanline repeating is sensitive to non-integral vertical resolution,
// so rounding is performed to eliminate it
// This won't be 100% accurate, but non-integral scaling isn't 100%
// accurate anyway
// Lower left (x,y+h)
myCoord[28] = 0.0f;
myCoord[29] = GLfloat(myImageH) / floor(((float)myImageH / myBaseH) + 0.5);
// Lower right (x+w,y+h)
myCoord[30] = 1.0f;
myCoord[31] = myCoord[29];
// Cache vertex and texture coordinates using vertex buffer object
if(myFB.myVBOAvailable)
{
myGL.BindBuffer(GL_ARRAY_BUFFER, myVBOID);
myGL.BufferData(GL_ARRAY_BUFFER, 32*sizeof(GLfloat), myCoord, GL_STATIC_DRAW);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::setTIAPalette(const uInt32* palette)
{
myFB.myNTSCFilter.setTIAPalette(myFB, palette);
}
#endif
+90
View File
@@ -0,0 +1,90 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FBSurfaceTIA.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef FB_SURFACE_TIA_HXX
#define FB_SURFACE_TIA_HXX
#ifdef DISPLAY_OPENGL
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferGL.hxx"
/**
A surface suitable for OpenGL rendering mode, but specifically for
rendering from a TIA source. It doesn't implement most of the
drawing primitives, since it's concerned with TIA images only.
This class extends FrameBuffer::FBSurface.
@author Stephen Anthony
*/
class FBSurfaceTIA : public FBSurface
{
friend class FrameBufferGL;
public:
FBSurfaceTIA(FrameBufferGL& buffer);
virtual ~FBSurfaceTIA();
// TIA surfaces don't implement most of the drawing primitives,
// only the methods absolutely necessary for dealing with drawing
// a TIA image
void getPos(uInt32& x, uInt32& y) const;
uInt32 getWidth() const { return myImageW; }
uInt32 getHeight() const { return myImageH; }
void translateCoords(Int32& x, Int32& y) const;
void update();
void invalidate();
void free();
void reload();
private:
void setTIA(const TIA& tia) { myTIA = &tia; }
void setTIAPalette(const uInt32* palette);
void enableScanlines(bool enable) { myScanlinesEnabled = enable; }
void setScanIntensity(uInt32 intensity);
void setTexInterpolation(bool enable);
void setScanInterpolation(bool enable);
void updateCoords(uInt32 baseH, uInt32 imgX, uInt32 imgY, uInt32 imgW, uInt32 imgH);
void updateCoords();
private:
FrameBufferGL& myFB;
const FrameBufferGL::GLpointers& myGL;
const TIA* myTIA;
SDL_Surface* myTexture;
uInt32 myPitch;
GLuint myTexID[2], myVBOID;
GLsizei myTexWidth;
GLsizei myTexHeight;
GLuint myBaseW, myBaseH;
GLuint myImageX, myImageY, myImageW, myImageH;
GLfloat myTexCoordW, myTexCoordH;
GLfloat myCoord[32];
GLint myTexFilter[2];
bool myScanlinesEnabled;
GLuint myScanlineIntensityI;
GLfloat myScanlineIntensityF;
};
#endif // DISPLAY_OPENGL
#endif
+64
View File
@@ -0,0 +1,64 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FSNodeFactory.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef FSNODE_FACTORY_HXX
#define FSNODE_FACTORY_HXX
class AbstractFSNode;
#if defined(UNIX) || defined(MAC_OSX)
#include "FSNodePOSIX.hxx"
#elif defined(WIN32)
#include "FSNodeWin32.hxx"
#else
#error Unsupported platform in FSNodeFactory!
#endif
#include "FSNodeZIP.hxx"
/**
This class deals with creating the different FSNode implementations.
I think you can see why this mess was put into a factory class :)
@author Stephen Anthony
*/
class FilesystemNodeFactory
{
public:
enum Type { SYSTEM, ZIP };
public:
static AbstractFSNode* create(const string& path, Type type)
{
switch(type)
{
case SYSTEM:
#if defined(UNIX) || defined(MAC_OSX)
return new FilesystemNodePOSIX(path);
#elif defined(WIN32)
return new FilesystemNodeWin32(path);
#endif
break;
case ZIP:
return new FilesystemNodeZIP(path);
break;
}
return 0;
}
};
#endif
+163
View File
@@ -0,0 +1,163 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FSNodeZIP.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "bspf.hxx"
#include "OSystem.hxx"
#include "FSNodeFactory.hxx"
#include "FSNodeZIP.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNodeZIP::FilesystemNodeZIP()
{
// We need a name, else the node is invalid
_path = _shortPath = _virtualFile = "";
_error = ZIPERR_NOT_A_FILE;
_numFiles = 0;
AbstractFSNode* tmp = 0;
_realNode = Common::SharedPtr<AbstractFSNode>(tmp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNodeZIP::FilesystemNodeZIP(const string& p)
{
_path = _shortPath = _virtualFile = "";
_error = ZIPERR_NOT_A_FILE;
// Extract ZIP file and virtual file (if specified)
size_t pos = BSPF_findIgnoreCase(p, ".zip");
if(pos == string::npos)
return;
_zipFile = p.substr(0, pos+4);
// Open file at least once to initialize the virtual file count
ZipHandler& zip = OSystem::zip(_zipFile);
_numFiles = zip.romFiles();
if(_numFiles == 0)
{
_error = ZIPERR_NO_ROMS;
return;
}
// We always need a virtual file
// Either one is given, or we use the first one
if(pos+5 < p.length())
_virtualFile = p.substr(pos+5);
else if(_numFiles == 1)
{
bool found = false;
while(zip.hasNext() && !found)
{
const std::string& file = zip.next();
if(BSPF_endsWithIgnoreCase(file, ".a26") ||
BSPF_endsWithIgnoreCase(file, ".bin") ||
BSPF_endsWithIgnoreCase(file, ".rom"))
{
_virtualFile = file;
found = true;
}
}
if(!found)
return;
}
AbstractFSNode* tmp =
FilesystemNodeFactory::create(_zipFile, FilesystemNodeFactory::SYSTEM);
_realNode = Common::SharedPtr<AbstractFSNode>(tmp);
setFlags(_zipFile, _virtualFile, _realNode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNodeZIP::FilesystemNodeZIP(const string& zipfile, const string& virtualfile,
Common::SharedPtr<AbstractFSNode> realnode)
{
setFlags(zipfile, virtualfile, realnode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FilesystemNodeZIP::setFlags(const string& zipfile,
const string& virtualfile,
Common::SharedPtr<AbstractFSNode> realnode)
{
_zipFile = zipfile;
_virtualFile = virtualfile;
_realNode = realnode;
_path = _realNode->getPath();
_shortPath = _realNode->getShortPath();
// Is a file component present?
if(_virtualFile.size() != 0)
{
_path += ("/" + _virtualFile);
_shortPath += ("/" + _virtualFile);
_numFiles = 1;
}
_error = ZIPERR_NONE;
if(!_realNode->isFile())
_error = ZIPERR_NOT_A_FILE;
if(!_realNode->isReadable())
_error = ZIPERR_NOT_READABLE;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode,
bool hidden) const
{
// Files within ZIP archives don't contain children
if(!isDirectory() || _error != ZIPERR_NONE)
return false;
ZipHandler& zip = OSystem::zip(_zipFile);
while(zip.hasNext())
{
FilesystemNodeZIP entry(_path, zip.next(), _realNode);
myList.push_back(new FilesystemNodeZIP(entry));
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FilesystemNodeZIP::read(uInt8*& image) const
{
switch(_error)
{
case ZIPERR_NONE: break;
case ZIPERR_NOT_A_FILE: throw "ZIP file contains errors/not found";
case ZIPERR_NOT_READABLE: throw "ZIP file not readable";
case ZIPERR_NO_ROMS: throw "ZIP file doesn't contain any ROMs";
}
ZipHandler& zip = OSystem::zip(_zipFile);
bool found = false;
while(zip.hasNext() && !found)
found = zip.next() == _virtualFile;
return found ? zip.decompress(image) : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFSNode* FilesystemNodeZIP::getParent() const
{
return _realNode ? _realNode->getParent() : 0;
}
+95
View File
@@ -0,0 +1,95 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FSNodeZIP.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef FS_NODE_ZIP_HXX
#define FS_NODE_ZIP_HXX
#include "StringList.hxx"
#include "FSNode.hxx"
/*
* Implementation of the Stella file system API based on ZIP archives.
* ZIP archives are treated as directories if the contain more than one ROM
* file, as a file if they contain a single ROM file, and as neither if the
* archive is empty. Hence, if a ZIP archive isn't a directory *or* a file,
* it is invalid.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class FilesystemNodeZIP : public AbstractFSNode
{
public:
/**
* Creates a FilesystemNodeZIP with the root node as path.
*/
FilesystemNodeZIP();
/**
* Creates a FilesystemNodeZIP for a given path.
*
* @param path String with the path the new node should point to.
* @param node Raw pointer to use for the internal FSNode
*/
FilesystemNodeZIP(const string& path);
bool exists() const { return _realNode && _realNode->exists(); }
const string& getName() const { return _virtualFile; }
const string& getPath() const { return _path; }
string getShortPath() const { return _shortPath; }
bool isDirectory() const { return _numFiles > 1; }
bool isFile() const { return _numFiles == 1; }
bool isReadable() const { return _realNode && _realNode->isReadable(); }
bool isWritable() const { return false; }
//////////////////////////////////////////////////////////
// For now, ZIP files cannot be modified in any way
bool makeDir() { return false; }
bool rename(const string& newfile) { return false; }
//////////////////////////////////////////////////////////
bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const;
AbstractFSNode* getParent() const;
uInt32 read(uInt8*& image) const;
private:
FilesystemNodeZIP(const string& zipfile, const string& virtualfile,
Common::SharedPtr<AbstractFSNode> realnode);
void setFlags(const string& zipfile, const string& virtualfile,
Common::SharedPtr<AbstractFSNode> realnode);
private:
/* Error types */
enum zip_error
{
ZIPERR_NONE = 0,
ZIPERR_NOT_A_FILE,
ZIPERR_NOT_READABLE,
ZIPERR_NO_ROMS
};
Common::SharedPtr<AbstractFSNode> _realNode;
string _zipFile, _virtualFile;
string _path, _shortPath;
zip_error _error;
uInt32 _numFiles;
};
#endif
+473
View File
@@ -0,0 +1,473 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifdef DISPLAY_OPENGL
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include <time.h>
#include <fstream>
#include "bspf.hxx"
#include "Console.hxx"
#include "Font.hxx"
#include "OSystem.hxx"
#include "Settings.hxx"
#include "TIA.hxx"
#include "FBSurfaceGL.hxx"
#include "FBSurfaceTIA.hxx"
#include "FrameBufferGL.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferGL::FrameBufferGL(OSystem* osystem)
: FrameBuffer(osystem),
myFilterType(kNormal),
myTiaSurface(NULL),
myDirtyFlag(true)
{
// We need a pixel format for palette value calculations
// It's done this way (vs directly accessing a FBSurfaceGL object)
// since the structure may be needed before any FBSurface's have
// been created
// Note: alpha disabled for now, since it's not used
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, 1, 32,
0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
myPixelFormat = *(s->format);
SDL_FreeSurface(s);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferGL::~FrameBufferGL()
{
// We're taking responsibility for this surface
delete myTiaSurface;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::loadLibrary(const string& library)
{
if(myLibraryLoaded)
return true;
// Try both the specified library and auto-detection
bool libLoaded = (library != "" && SDL_GL_LoadLibrary(library.c_str()) >= 0);
bool autoLoaded = false;
if(!libLoaded) autoLoaded = (SDL_GL_LoadLibrary(0) >= 0);
if(!libLoaded && !autoLoaded)
return false;
return myLibraryLoaded = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::loadFuncs(GLFunctionality functionality)
{
#define OGL_INIT(NAME,RET,FUNC,PARAMS) \
p_gl.NAME = (RET(APIENTRY*)PARAMS) SDL_GL_GetProcAddress(#FUNC); if(!p_gl.NAME) return false
if(myLibraryLoaded)
{
// Fill the function pointers for GL functions
// If anything fails, we'll know it immediately, and return false
switch(functionality)
{
case kGL_BASIC:
OGL_INIT(Clear,void,glClear,(GLbitfield));
OGL_INIT(Enable,void,glEnable,(GLenum));
OGL_INIT(Disable,void,glDisable,(GLenum));
OGL_INIT(PushAttrib,void,glPushAttrib,(GLbitfield));
OGL_INIT(GetString,const GLubyte*,glGetString,(GLenum));
OGL_INIT(Hint,void,glHint,(GLenum, GLenum));
OGL_INIT(ShadeModel,void,glShadeModel,(GLenum));
OGL_INIT(MatrixMode,void,glMatrixMode,(GLenum));
OGL_INIT(Ortho,void,glOrtho,(GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble));
OGL_INIT(Viewport,void,glViewport,(GLint, GLint, GLsizei, GLsizei));
OGL_INIT(LoadIdentity,void,glLoadIdentity,(void));
OGL_INIT(Translatef,void,glTranslatef,(GLfloat,GLfloat,GLfloat));
OGL_INIT(EnableClientState,void,glEnableClientState,(GLenum));
OGL_INIT(DisableClientState,void,glDisableClientState,(GLenum));
OGL_INIT(VertexPointer,void,glVertexPointer,(GLint,GLenum,GLsizei,const GLvoid*));
OGL_INIT(TexCoordPointer,void,glTexCoordPointer,(GLint,GLenum,GLsizei,const GLvoid*));
OGL_INIT(DrawArrays,void,glDrawArrays,(GLenum,GLint,GLsizei));
OGL_INIT(ReadPixels,void,glReadPixels,(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*));
OGL_INIT(PixelStorei,void,glPixelStorei,(GLenum, GLint));
OGL_INIT(TexEnvf,void,glTexEnvf,(GLenum, GLenum, GLfloat));
OGL_INIT(GenTextures,void,glGenTextures,(GLsizei, GLuint*));
OGL_INIT(DeleteTextures,void,glDeleteTextures,(GLsizei, const GLuint*));
OGL_INIT(ActiveTexture,void,glActiveTexture,(GLenum));
OGL_INIT(BindTexture,void,glBindTexture,(GLenum, GLuint));
OGL_INIT(TexImage2D,void,glTexImage2D,(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*));
OGL_INIT(TexSubImage2D,void,glTexSubImage2D,(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*));
OGL_INIT(TexParameteri,void,glTexParameteri,(GLenum, GLenum, GLint));
OGL_INIT(GetError,GLenum,glGetError,(void));
OGL_INIT(Color4f,void,glColor4f,(GLfloat,GLfloat,GLfloat,GLfloat));
OGL_INIT(BlendFunc,void,glBlendFunc,(GLenum,GLenum));
break; // kGL_Full
case kGL_VBO:
OGL_INIT(GenBuffers,void,glGenBuffers,(GLsizei,GLuint*));
OGL_INIT(BindBuffer,void,glBindBuffer,(GLenum,GLuint));
OGL_INIT(BufferData,void,glBufferData,(GLenum,GLsizei,const void*,GLenum));
OGL_INIT(DeleteBuffers,void,glDeleteBuffers,(GLsizei, const GLuint*));
break; // kGL_VBO
}
}
else
return false;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::initSubsystem(VideoMode& mode)
{
mySDLFlags |= SDL_OPENGL;
// Set up the OpenGL attributes
myDepth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
switch(myDepth)
{
case 15:
case 16:
myRGB[0] = 5; myRGB[1] = 5; myRGB[2] = 5; myRGB[3] = 0;
break;
case 24:
case 32:
myRGB[0] = 8; myRGB[1] = 8; myRGB[2] = 8; myRGB[3] = 0;
break;
default: // This should never happen
return false;
break;
}
// Create the screen
if(!setVidMode(mode))
return false;
// Now check to see what color components were actually created
SDL_GL_GetAttribute( SDL_GL_RED_SIZE, &myRGB[0] );
SDL_GL_GetAttribute( SDL_GL_GREEN_SIZE, &myRGB[1] );
SDL_GL_GetAttribute( SDL_GL_BLUE_SIZE, &myRGB[2] );
SDL_GL_GetAttribute( SDL_GL_ALPHA_SIZE, &myRGB[3] );
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FrameBufferGL::about() const
{
ostringstream out;
out << "Video rendering: OpenGL mode" << endl
<< " Vendor: " << p_gl.GetString(GL_VENDOR) << endl
<< " Renderer: " << p_gl.GetString(GL_RENDERER) << endl
<< " Version: " << p_gl.GetString(GL_VERSION) << endl
<< " Color: " << myDepth << " bit, " << myRGB[0] << "-"
<< myRGB[1] << "-" << myRGB[2] << "-" << myRGB[3] << ", "
<< "GL_BGRA" << endl
<< " Extensions: VBO " << (myVBOAvailable ? "enabled" : "disabled")
<< endl;
return out.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::setVidMode(VideoMode& mode)
{
bool inTIAMode =
myOSystem->eventHandler().state() != EventHandler::S_LAUNCHER &&
myOSystem->eventHandler().state() != EventHandler::S_DEBUGGER;
// Grab the initial height before it's updated below
// We need it for the creating the TIA surface
uInt32 baseHeight = mode.image_h / mode.gfxmode.zoom;
// Aspect ratio and fullscreen stretching only applies to the TIA
if(inTIAMode)
{
// Aspect ratio (depends on whether NTSC or PAL is detected)
// Not available in 'small' resolutions
if(myOSystem->desktopWidth() >= 640)
{
const string& frate = myOSystem->console().about().InitialFrameRate;
int aspect =
myOSystem->settings().getInt(frate == "60" ? "gl_aspectn" : "gl_aspectp");
mode.image_w = (uInt16)(float(mode.image_w * aspect) / 100.0);
}
// Fullscreen mode stretching
if(fullScreen() &&
(mode.image_w < mode.screen_w) && (mode.image_h < mode.screen_h))
{
float stretchFactor = 1.0;
float scaleX = float(mode.image_w) / mode.screen_w;
float scaleY = float(mode.image_h) / mode.screen_h;
// Scale to actual or integral factors
if(myOSystem->settings().getBool("gl_fsscale"))
{
// Scale to full (non-integral) available space
if(scaleX > scaleY)
stretchFactor = float(mode.screen_w) / mode.image_w;
else
stretchFactor = float(mode.screen_h) / mode.image_h;
}
else
{
// Only scale to an integral amount
if(scaleX > scaleY)
{
int bw = mode.image_w / mode.gfxmode.zoom;
stretchFactor = float(int(mode.screen_w / bw) * bw) / mode.image_w;
}
else
{
int bh = mode.image_h / mode.gfxmode.zoom;
stretchFactor = float(int(mode.screen_h / bh) * bh) / mode.image_h;
}
}
mode.image_w = (Uint16) (stretchFactor * mode.image_w);
mode.image_h = (Uint16) (stretchFactor * mode.image_h);
}
}
// Now re-calculate the dimensions
if(!fullScreen()) mode.screen_w = mode.image_w;
mode.image_x = (mode.screen_w - mode.image_w) >> 1;
mode.image_y = (mode.screen_h - mode.image_h) >> 1;
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, myRGB[0] );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, myRGB[1] );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, myRGB[2] );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, myRGB[3] );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
// There's no guarantee this is supported on all hardware
// We leave it to the user to test and decide
int vsync = myOSystem->settings().getBool("gl_vsync") ? 1 : 0;
SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, vsync );
// Create screen containing GL context
myScreen = SDL_SetVideoMode(mode.screen_w, mode.screen_h, 0, mySDLFlags);
if(myScreen == NULL)
{
cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
return false;
}
// Make sure the flags represent the current screen state
mySDLFlags = myScreen->flags;
// Load OpenGL function pointers
if(loadFuncs(kGL_BASIC))
myVBOAvailable = myOSystem->settings().getBool("gl_vbo") && loadFuncs(kGL_VBO);
else
return false;
// Optimization hints
p_gl.ShadeModel(GL_FLAT);
p_gl.Disable(GL_CULL_FACE);
p_gl.Disable(GL_DEPTH_TEST);
p_gl.Disable(GL_ALPHA_TEST);
p_gl.Disable(GL_LIGHTING);
p_gl.Hint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
// Initialize GL display
p_gl.Viewport(0, 0, mode.screen_w, mode.screen_h);
p_gl.MatrixMode(GL_PROJECTION);
p_gl.LoadIdentity();
p_gl.Ortho(0.0, mode.screen_w, mode.screen_h, 0.0, -1.0, 1.0);
p_gl.MatrixMode(GL_MODELVIEW);
p_gl.LoadIdentity();
p_gl.Translatef(0.375, 0.375, 0.0); // fix scanline mis-draw issues
//cerr << "dimensions: " << (fullScreen() ? "(full)" : "") << endl << mode << endl;
// The framebuffer only takes responsibility for TIA surfaces
// Other surfaces (such as the ones used for dialogs) are allocated
// in the Dialog class
if(inTIAMode)
{
// Since we have free hardware stretching, the base TIA surface is created
// only once, and its texture coordinates changed when we want to draw a
// smaller or larger image
if(!myTiaSurface)
myTiaSurface = new FBSurfaceTIA(*this);
myTiaSurface->updateCoords(baseHeight, mode.image_x, mode.image_y,
mode.image_w, mode.image_h);
myTiaSurface->enableScanlines(ntscEnabled());
myTiaSurface->setScanIntensity(myOSystem->settings().getInt("tv_scanlines"));
myTiaSurface->setTexInterpolation(myOSystem->settings().getBool("gl_inter"));
myTiaSurface->setScanInterpolation(myOSystem->settings().getBool("tv_scaninter"));
myTiaSurface->setTIA(myOSystem->console().tia());
}
// Any previously allocated textures currently in use by various UI items
// need to be refreshed as well (only seems to be required for OSX)
resetSurfaces(myTiaSurface);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::invalidate()
{
p_gl.Clear(GL_COLOR_BUFFER_BIT);
if(myTiaSurface)
myTiaSurface->invalidate();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawTIA(bool fullRedraw)
{
// The TIA surface takes all responsibility for drawing
myTiaSurface->update();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::postFrameUpdate()
{
if(myDirtyFlag)
{
// Now show all changes made to the texture(s)
SDL_GL_SwapBuffers();
myDirtyFlag = false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::enablePhosphor(bool enable, int blend)
{
if(myTiaSurface)
{
myUsePhosphor = enable;
myPhosphorBlend = blend;
myFilterType = FilterType(enable ? myFilterType | 0x01 : myFilterType & 0x10);
myRedrawEntireFrame = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::enableNTSC(bool enable)
{
if(myTiaSurface)
{
myFilterType = FilterType(enable ? myFilterType | 0x10 : myFilterType & 0x01);
myTiaSurface->updateCoords();
myTiaSurface->enableScanlines(ntscEnabled());
myTiaSurface->setScanIntensity(myOSystem->settings().getInt("tv_scanlines"));
myTiaSurface->setTexInterpolation(myOSystem->settings().getBool("gl_inter"));
myTiaSurface->setScanInterpolation(myOSystem->settings().getBool("tv_scaninter"));
myRedrawEntireFrame = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBufferGL::enableScanlines(int relative, int absolute)
{
int intensity = myTiaSurface->myScanlineIntensityI;
if(myTiaSurface)
{
if(relative == 0) intensity = absolute;
else intensity += relative;
intensity = BSPF_max(0, intensity);
intensity = BSPF_min(100, intensity);
myTiaSurface->setScanIntensity(intensity);
myRedrawEntireFrame = true;
}
return intensity;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::enableScanlineInterpolation(bool enable)
{
if(myTiaSurface)
{
myTiaSurface->setScanInterpolation(enable);
myRedrawEntireFrame = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::setTIAPalette(const uInt32* palette)
{
FrameBuffer::setTIAPalette(palette);
myTiaSurface->setTIAPalette(palette);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurface* FrameBufferGL::createSurface(int w, int h, bool isBase) const
{
// Ignore 'isBase' argument; all GL surfaces are separate
// Also, this method will only be called for use in external dialogs.
// and never used for TIA surfaces
return new FBSurfaceGL((FrameBufferGL&)*this, w, h);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::scanline(uInt32 row, uInt8* data) const
{
// Invert the row, since OpenGL rows start at the bottom
// of the framebuffer
const GUI::Rect& image = imageRect();
row = image.height() + image.y() - row - 1;
p_gl.PixelStorei(GL_PACK_ALIGNMENT, 1);
p_gl.ReadPixels(image.x(), row, image.width(), 1, GL_RGB, GL_UNSIGNED_BYTE, data);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FrameBufferGL::effectsInfo() const
{
ostringstream buf;
switch(myFilterType)
{
case kNormal:
buf << "Disabled, normal mode";
break;
case kPhosphor:
buf << "Disabled, phosphor mode";
break;
case kBlarggNormal:
buf << myNTSCFilter.getPreset() << ", scanlines="
<< myTiaSurface->myScanlineIntensityI << "/"
<< (myTiaSurface->myTexFilter[1] == GL_LINEAR ? "inter" : "nointer");
break;
case kBlarggPhosphor:
buf << myNTSCFilter.getPreset() << ", phosphor, scanlines="
<< myTiaSurface->myScanlineIntensityI << "/"
<< (myTiaSurface->myTexFilter[1] == GL_LINEAR ? "inter" : "nointer");
break;
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::myLibraryLoaded = false;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::myVBOAvailable = false;
#endif // DISPLAY_OPENGL
+285
View File
@@ -0,0 +1,285 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef FRAMEBUFFER_GL_HXX
#define FRAMEBUFFER_GL_HXX
#ifdef DISPLAY_OPENGL
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_syswm.h>
class OSystem;
class FBSurfaceGL;
class FBSurfaceTIA;
class TIA;
#include "bspf.hxx"
#include "FrameBuffer.hxx"
/**
This class implements an SDL OpenGL framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferGL.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class FrameBufferGL : public FrameBuffer
{
friend class FBSurfaceGL;
friend class FBSurfaceTIA;
public:
/**
Creates a new OpenGL framebuffer
*/
FrameBufferGL(OSystem* osystem);
/**
Destructor
*/
virtual ~FrameBufferGL();
/**
Check if OpenGL is available on this system, and then opens it.
If any errors occur, we shouldn't attempt to instantiate a
FrameBufferGL object.
@param library The filename of the OpenGL library
*/
static bool loadLibrary(const string& library);
//////////////////////////////////////////////////////////////////////
// The following are derived from public methods in FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
Enable/disable phosphor effect.
*/
void enablePhosphor(bool enable, int blend);
/**
Enable/disable NTSC filtering effects.
*/
void enableNTSC(bool enable);
bool ntscEnabled() const { return myFilterType & 0x10; }
/**
Set up the TIA/emulation palette for a screen of any depth > 8.
@param palette The array of colors
*/
void setTIAPalette(const uInt32* palette);
/**
This method is called to retrieve the R/G/B data from the given pixel.
@param pixel The pixel containing R/G/B data
@param r The red component of the color
@param g The green component of the color
@param b The blue component of the color
*/
void getRGB(Uint32 pixel, Uint8* r, Uint8* g, Uint8* b) const
{ SDL_GetRGB(pixel, (SDL_PixelFormat*)&myPixelFormat, r, g, b); }
/**
This method is called to map a given R/G/B triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b) const
{ return SDL_MapRGB((SDL_PixelFormat*)&myPixelFormat, r, g, b); }
/**
This method is called to query the type of the FrameBuffer.
*/
BufferType type() const { return kDoubleBuffer; }
/**
This method is called to query the TV effects in use by the FrameBuffer.
*/
string effectsInfo() const;
/**
This method is called to get the specified scanline data.
@param row The row we are looking for
@param data The actual pixel data (in bytes)
*/
void scanline(uInt32 row, uInt8* data) const;
protected:
//////////////////////////////////////////////////////////////////////
// The following are derived from protected methods in FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This method is called to initialize the video subsystem
with the given video mode. Normally, it will also call setVidMode().
@param mode The video mode to use
@return False on any errors, else true
*/
bool initSubsystem(VideoMode& mode);
/**
This method is called to change to the given video mode. If the mode
is successfully changed, 'mode' holds the actual dimensions used.
@param mode The video mode to use
@return False on any errors (in which case 'mode' is invalid), else true
*/
bool setVidMode(VideoMode& mode);
/**
This method is called to invalidate the contents of the entire
framebuffer (ie, mark the current content as invalid, and erase it on
the next drawing pass).
*/
void invalidate();
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
FBSurface* createSurface(int w, int h, bool useBase = false) const;
/**
This method should be called anytime the TIA needs to be redrawn
to the screen (full indicating that a full redraw is required).
*/
void drawTIA(bool full);
/**
This method is called to provide information about the FrameBuffer.
*/
string about() const;
/**
This method is called after any drawing is done (per-frame).
*/
void postFrameUpdate();
/**
Change scanline intensity and interpolation.
@param relative If non-zero, change current intensity by
'relative' amount, otherwise set to 'absolute'
@return New current intensity
*/
uInt32 enableScanlines(int relative, int absolute = 50);
void enableScanlineInterpolation(bool enable);
private:
enum GLFunctionality {
kGL_BASIC, kGL_VBO
};
bool loadFuncs(GLFunctionality functionality);
// Enumeration created such that phosphor off/on is in LSB,
// and Blargg off/on is in MSB
enum FilterType {
kNormal = 0x00,
kPhosphor = 0x01,
kBlarggNormal = 0x10,
kBlarggPhosphor = 0x11
};
FilterType myFilterType;
static uInt32 power_of_two(uInt32 input)
{
uInt32 value = 1;
while( value < input )
value <<= 1;
return value;
}
private:
// The lower-most base surface (will always be a TIA surface,
// since Dialog surfaces are allocated by the Dialog class directly).
FBSurfaceTIA* myTiaSurface;
// Used by mapRGB (when palettes are created)
SDL_PixelFormat myPixelFormat;
// The depth of the texture buffer
uInt32 myDepth;
// The size of color components for OpenGL
Int32 myRGB[4];
// Indicates that the texture has been modified, and should be redrawn
bool myDirtyFlag;
// Indicates if the OpenGL library has been properly loaded
static bool myLibraryLoaded;
// Indicates whether Vertex Buffer Objects (VBO) are available
static bool myVBOAvailable;
// Structure containing dynamically-loaded OpenGL function pointers
#define OGL_DECLARE(NAME,RET,FUNC,PARAMS) RET (APIENTRY* NAME) PARAMS
typedef struct {
OGL_DECLARE(Clear,void,glClear,(GLbitfield));
OGL_DECLARE(Enable,void,glEnable,(GLenum));
OGL_DECLARE(Disable,void,glDisable,(GLenum));
OGL_DECLARE(PushAttrib,void,glPushAttrib,(GLbitfield));
OGL_DECLARE(GetString,const GLubyte*,glGetString,(GLenum));
OGL_DECLARE(Hint,void,glHint,(GLenum, GLenum));
OGL_DECLARE(ShadeModel,void,glShadeModel,(GLenum));
OGL_DECLARE(MatrixMode,void,glMatrixMode,(GLenum));
OGL_DECLARE(Ortho,void,glOrtho,(GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble));
OGL_DECLARE(Viewport,void,glViewport,(GLint, GLint, GLsizei, GLsizei));
OGL_DECLARE(LoadIdentity,void,glLoadIdentity,(void));
OGL_DECLARE(Translatef,void,glTranslatef,(GLfloat,GLfloat,GLfloat));
OGL_DECLARE(EnableClientState,void,glEnableClientState,(GLenum));
OGL_DECLARE(DisableClientState,void,glDisableClientState,(GLenum));
OGL_DECLARE(VertexPointer,void,glVertexPointer,(GLint,GLenum,GLsizei,const GLvoid*));
OGL_DECLARE(TexCoordPointer,void,glTexCoordPointer,(GLint,GLenum,GLsizei,const GLvoid*));
OGL_DECLARE(DrawArrays,void,glDrawArrays,(GLenum,GLint,GLsizei));
OGL_DECLARE(ReadPixels,void,glReadPixels,(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*));
OGL_DECLARE(PixelStorei,void,glPixelStorei,(GLenum, GLint));
OGL_DECLARE(TexEnvf,void,glTexEnvf,(GLenum, GLenum, GLfloat));
OGL_DECLARE(GenTextures,void,glGenTextures,(GLsizei, GLuint*));
OGL_DECLARE(DeleteTextures,void,glDeleteTextures,(GLsizei, const GLuint*));
OGL_DECLARE(ActiveTexture,void,glActiveTexture,(GLenum));
OGL_DECLARE(BindTexture,void,glBindTexture,(GLenum, GLuint));
OGL_DECLARE(TexImage2D,void,glTexImage2D,(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*));
OGL_DECLARE(TexSubImage2D,void,glTexSubImage2D,(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*));
OGL_DECLARE(TexParameteri,void,glTexParameteri,(GLenum, GLenum, GLint));
OGL_DECLARE(GetError,GLenum,glGetError,(void));
OGL_DECLARE(Color4f,void,glColor4f,(GLfloat,GLfloat,GLfloat,GLfloat));
OGL_DECLARE(BlendFunc,void,glBlendFunc,(GLenum,GLenum));
OGL_DECLARE(GenBuffers,void,glGenBuffers,(GLsizei,GLuint*));
OGL_DECLARE(BindBuffer,void,glBindBuffer,(GLenum,GLuint));
OGL_DECLARE(BufferData,void,glBufferData,(GLenum,GLsizei,const void*,GLenum));
OGL_DECLARE(DeleteBuffers,void,glDeleteBuffers,(GLsizei, const GLuint*));
} GLpointers;
GLpointers p_gl;
};
#endif // DISPLAY_OPENGL
#endif
+860
View File
@@ -0,0 +1,860 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <sstream>
#include <SDL.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "Font.hxx"
#include "OSystem.hxx"
#include "RectList.hxx"
#include "Settings.hxx"
#include "TIA.hxx"
#include "FrameBufferSoft.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::FrameBufferSoft(OSystem* osystem)
: FrameBuffer(osystem),
myZoomLevel(2),
myRenderType(kSoftZoom_16),
myTiaDirty(false),
myInUIMode(false),
myRectList(NULL)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::~FrameBufferSoft()
{
delete myRectList;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSoft::initSubsystem(VideoMode& mode)
{
// Set up the rectangle list to be used in the dirty update
delete myRectList;
myRectList = new RectList();
if(!myRectList)
{
myOSystem->logMessage("ERROR: Unable to get memory for SDL rects", 0);
return false;
}
// Create the screen
return setVidMode(mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FrameBufferSoft::about() const
{
ostringstream buf;
buf << "Video rendering: Software mode" << endl << setfill('0')
<< " Color: " << (int)myFormat->BitsPerPixel << " bit" << endl
<< " Rmask = " << hex << setw(8) << (int)myFormat->Rmask
<< ", Rshift = "<< dec << setw(2) << (int)myFormat->Rshift
<< ", Rloss = " << dec << setw(2) << (int)myFormat->Rloss << endl
<< " Gmask = " << hex << setw(8) << (int)myFormat->Gmask
<< ", Gshift = "<< dec << setw(2) << (int)myFormat->Gshift
<< ", Gloss = " << dec << setw(2) << (int)myFormat->Gloss << endl
<< " Bmask = " << hex << setw(8) << (int)myFormat->Bmask
<< ", Bshift = "<< dec << setw(2) << (int)myFormat->Bshift
<< ", Bloss = " << dec << setw(2) << (int)myFormat->Bloss << endl;
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSoft::setVidMode(VideoMode& mode)
{
// Make sure to clear the screen
if(myScreen)
{
SDL_FillRect(myScreen, NULL, 0);
SDL_UpdateRect(myScreen, 0, 0, 0, 0);
}
myScreen = SDL_SetVideoMode(mode.screen_w, mode.screen_h, 0, mySDLFlags);
if(myScreen == NULL)
{
ostringstream buf;
buf << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
myOSystem->logMessage(buf.str(), 0);
return false;
}
myFormat = myScreen->format;
myBytesPerPixel = myFormat->BytesPerPixel;
// Make sure the flags represent the current screen state
mySDLFlags = myScreen->flags;
// Make sure drawTIA() knows which renderer to use
switch(myBytesPerPixel)
{
case 2: // 16-bit
myPitch = myScreen->pitch >> 1;
myRenderType = myUsePhosphor ? kPhosphor_16 : kSoftZoom_16;
break;
case 3: // 24-bit
myPitch = myScreen->pitch;
myRenderType = myUsePhosphor ? kPhosphor_24 : kSoftZoom_24;
break;
case 4: // 32-bit
myPitch = myScreen->pitch >> 2;
myRenderType = myUsePhosphor ? kPhosphor_32 : kSoftZoom_32;
break;
}
myBaseOffset = mode.image_y * myPitch + mode.image_x;
// If software mode can open the given screen, it will always be in the
// requested format, or not at all; we only update mode when the screen
// is successfully created
mode.screen_w = myScreen->w;
mode.screen_h = myScreen->h;
myZoomLevel = mode.gfxmode.zoom;
// Erase old rects, since they've probably been scaled for
// a different sized screen
myRectList->start();
// Any previously allocated surfaces have probably changed as well,
// so we should refresh them
resetSurfaces();
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::invalidate()
{
if(myScreen)
SDL_FillRect(myScreen, NULL, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawTIA(bool fullRedraw)
{
const TIA& tia = myOSystem->console().tia();
uInt8* currentFrame = tia.currentFrameBuffer();
uInt8* previousFrame = tia.previousFrameBuffer();
uInt32 width = tia.width();
uInt32 height = tia.height();
switch(myRenderType)
{
case kSoftZoom_16:
{
SDL_LockSurface(myScreen);
uInt16* buffer = (uInt16*)myScreen->pixels + myBaseOffset;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
if(v != w || fullRedraw)
{
while(xstride--)
{
buffer[pos++] = (uInt16) myDefPalette[v];
buffer[pos++] = (uInt16) myDefPalette[v];
}
myTiaDirty = true;
}
else
pos += xstride + xstride;
}
screenofsY += myPitch;
}
bufofsY += width;
}
SDL_UnlockSurface(myScreen);
break; // kSoftZoom_16
}
case kSoftZoom_24:
{
SDL_LockSurface(myScreen);
uInt8* buffer = (uInt8*)myScreen->pixels + myBaseOffset;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
if(v != w || fullRedraw)
{
uInt8 a = myDefPalette24[v][0],
b = myDefPalette24[v][1],
c = myDefPalette24[v][2];
while(xstride--)
{
buffer[pos++] = a; buffer[pos++] = b; buffer[pos++] = c;
buffer[pos++] = a; buffer[pos++] = b; buffer[pos++] = c;
}
myTiaDirty = true;
}
else // try to eliminate multiply whereever possible
pos += xstride + xstride + xstride + xstride + xstride + xstride;
}
screenofsY += myPitch;
}
bufofsY += width;
}
SDL_UnlockSurface(myScreen);
break; // kSoftZoom_24
}
case kSoftZoom_32:
{
SDL_LockSurface(myScreen);
uInt32* buffer = (uInt32*)myScreen->pixels + myBaseOffset;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
if(v != w || fullRedraw)
{
while(xstride--)
{
buffer[pos++] = (uInt32) myDefPalette[v];
buffer[pos++] = (uInt32) myDefPalette[v];
}
myTiaDirty = true;
}
else
pos += xstride + xstride;
}
screenofsY += myPitch;
}
bufofsY += width;
}
SDL_UnlockSurface(myScreen);
break; // kSoftZoom_32
}
case kPhosphor_16:
{
SDL_LockSurface(myScreen);
uInt16* buffer = (uInt16*)myScreen->pixels + myBaseOffset;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
while(xstride--)
{
buffer[pos++] = (uInt16) myAvgPalette[v][w];
buffer[pos++] = (uInt16) myAvgPalette[v][w];
}
}
screenofsY += myPitch;
}
bufofsY += width;
}
SDL_UnlockSurface(myScreen);
myTiaDirty = true;
break; // kPhosphor_16
}
case kPhosphor_24:
{
SDL_LockSurface(myScreen);
uInt8* buffer = (uInt8*)myScreen->pixels + myBaseOffset;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
uInt8 a, b, c;
uInt32 pixel = myAvgPalette[v][w];
if(SDL_BYTEORDER == SDL_LIL_ENDIAN)
{
a = (pixel & myFormat->Bmask) >> myFormat->Bshift;
b = (pixel & myFormat->Gmask) >> myFormat->Gshift;
c = (pixel & myFormat->Rmask) >> myFormat->Rshift;
}
else
{
a = (pixel & myFormat->Rmask) >> myFormat->Rshift;
b = (pixel & myFormat->Gmask) >> myFormat->Gshift;
c = (pixel & myFormat->Bmask) >> myFormat->Bshift;
}
while(xstride--)
{
buffer[pos++] = a; buffer[pos++] = b; buffer[pos++] = c;
buffer[pos++] = a; buffer[pos++] = b; buffer[pos++] = c;
}
}
screenofsY += myPitch;
}
bufofsY += width;
}
SDL_UnlockSurface(myScreen);
myTiaDirty = true;
break; // kPhosphor_24
}
case kPhosphor_32:
{
SDL_LockSurface(myScreen);
uInt32* buffer = (uInt32*)myScreen->pixels + myBaseOffset;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
while(xstride--)
{
buffer[pos++] = (uInt32) myAvgPalette[v][w];
buffer[pos++] = (uInt32) myAvgPalette[v][w];
}
}
screenofsY += myPitch;
}
bufofsY += width;
}
SDL_UnlockSurface(myScreen);
myTiaDirty = true;
break; // kPhosphor_32
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::postFrameUpdate()
{
if(myTiaDirty && !myInUIMode)
{
SDL_UpdateRect(myScreen, 0, 0, 0, 0);
myTiaDirty = false;
}
else if(myRectList->numRects() > 0)
{
//myRectList->print(myScreen->w, myScreen->h);
SDL_UpdateRects(myScreen, myRectList->numRects(), myRectList->rects());
}
myRectList->start();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::enablePhosphor(bool enable, int blend)
{
myUsePhosphor = enable;
myPhosphorBlend = blend;
// Make sure drawMediaSource() knows which renderer to use
switch(myBytesPerPixel)
{
case 2: // 16-bit
myRenderType = myUsePhosphor ? kPhosphor_16 : kSoftZoom_16;
break;
case 3: // 24-bit
myRenderType = myUsePhosphor ? kPhosphor_24 : kSoftZoom_24;
break;
case 4: // 32-bit
myRenderType = myUsePhosphor ? kPhosphor_32 : kSoftZoom_32;
break;
}
myRedrawEntireFrame = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurface* FrameBufferSoft::createSurface(int w, int h, bool isBase) const
{
// For some unknown reason, OSX in software fullscreen mode doesn't like
// to use the underlying surface directly
// I suspect it's an SDL compatibility thing, since I get errors
// referencing Quartz vs. QuickDraw, and then a program crash
// For now, we'll just always use entire surfaces for OSX
// I don't think this will have much effect, since OpenGL mode is the
// preferred method in OSX (basically, all OSX installations have OpenGL
// support)
#ifdef MAC_OSX
isBase = false;
#endif
SDL_Surface* surface = isBase ? myScreen :
SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, myFormat->BitsPerPixel,
myFormat->Rmask, myFormat->Gmask, myFormat->Bmask,
myFormat->Amask);
return new FBSurfaceSoft(*this, surface, w, h, isBase);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::scanline(uInt32 row, uInt8* data) const
{
// Make sure no pixels are being modified
SDL_LockSurface(myScreen);
uInt32 pixel = 0;
uInt8 *p, r, g, b;
// Row will be offset by the amount the actual image is shifted down
const GUI::Rect& image = imageRect();
row += image.y();
for(Int32 x = 0; x < myScreen->w; ++x)
{
p = (Uint8*) ((uInt8*)myScreen->pixels + // Start at top of RAM
(row * myScreen->pitch) + // Go down 'row' lines
((x + image.x()) * myBytesPerPixel)); // Go in 'x' pixels
switch(myBytesPerPixel)
{
case 1:
pixel = *p;
break;
case 2:
pixel = *(Uint16*) p;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
pixel = p[0] << 16 | p[1] << 8 | p[2];
else
pixel = p[0] | p[1] << 8 | p[2] << 16;
break;
case 4:
pixel = *(Uint32*) p;
break;
}
SDL_GetRGB(pixel, myScreen->format, &r, &g, &b);
data[x * 3 + 0] = r;
data[x * 3 + 1] = g;
data[x * 3 + 2] = b;
}
SDL_UnlockSurface(myScreen);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FBSurfaceSoft implementation follows ...
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSoft::FBSurfaceSoft(const FrameBufferSoft& buffer, SDL_Surface* surface,
uInt32 w, uInt32 h, bool isBase)
: myFB(buffer),
mySurface(surface),
myWidth(w),
myHeight(h),
myIsBaseSurface(isBase),
mySurfaceIsDirty(false),
myPitch(0),
myXOrig(0),
myYOrig(0),
myXOffset(0),
myYOffset(0)
{
reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSoft::~FBSurfaceSoft()
{
if(!myIsBaseSurface)
SDL_FreeSurface(mySurface);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color)
{
// Horizontal line
SDL_Rect tmp;
tmp.x = x + myXOffset;
tmp.y = y + myYOffset;
tmp.w = x2 - x + 1;
tmp.h = 1;
SDL_FillRect(mySurface, &tmp, myFB.myDefPalette[color]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::vLine(uInt32 x, uInt32 y, uInt32 y2, uInt32 color)
{
// Vertical line
SDL_Rect tmp;
tmp.x = x + myXOffset;
tmp.y = y + myYOffset;
tmp.w = 1;
tmp.h = y2 - y + 1;
SDL_FillRect(mySurface, &tmp, myFB.myDefPalette[color]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color)
{
// Fill the rectangle
SDL_Rect tmp;
tmp.x = x + myXOffset;
tmp.y = y + myYOffset;
tmp.w = w;
tmp.h = h;
SDL_FillRect(mySurface, &tmp, myFB.myDefPalette[color]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::drawChar(const GUI::Font& font, uInt8 chr,
uInt32 tx, uInt32 ty, uInt32 color)
{
const FontDesc& desc = font.desc();
// If this character is not included in the font, use the default char.
if(chr < desc.firstchar || chr >= desc.firstchar + desc.size)
{
if (chr == ' ') return;
chr = desc.defaultchar;
}
chr -= desc.firstchar;
// Get the bounding box of the character
int bbw, bbh, bbx, bby;
if(!desc.bbx)
{
bbw = desc.fbbw;
bbh = desc.fbbh;
bbx = desc.fbbx;
bby = desc.fbby;
}
else
{
bbw = desc.bbx[chr].w;
bbh = desc.bbx[chr].h;
bbx = desc.bbx[chr].x;
bby = desc.bbx[chr].y;
}
const uInt16* tmp = desc.bits + (desc.offset ? desc.offset[chr] : (chr * desc.fbbh));
switch(myFB.myBytesPerPixel)
{
case 2:
{
// Get buffer position where upper-left pixel of the character will be drawn
uInt16* buffer = (uInt16*)getBasePtr(tx + bbx, ty + desc.ascent - bby - bbh);
for(int y = 0; y < bbh; y++)
{
const uInt16 ptr = *tmp++;
uInt16 mask = 0x8000;
for(int x = 0; x < bbw; x++, mask >>= 1)
if(ptr & mask)
buffer[x] = (uInt16) myFB.myDefPalette[color];
buffer += myPitch;
}
break;
}
case 3:
{
// Get buffer position where upper-left pixel of the character will be drawn
uInt8* buffer = (uInt8*)getBasePtr(tx + bbx, ty + desc.ascent - bby - bbh);
uInt8 a = myFB.myDefPalette24[color][0],
b = myFB.myDefPalette24[color][1],
c = myFB.myDefPalette24[color][2];
for(int y = 0; y < bbh; y++, buffer += myPitch)
{
const uInt16 ptr = *tmp++;
uInt16 mask = 0x8000;
uInt8* buf_ptr = buffer;
for(int x = 0; x < bbw; x++, mask >>= 1)
{
if(ptr & mask)
{
*buf_ptr++ = a; *buf_ptr++ = b; *buf_ptr++ = c;
}
else
buf_ptr += 3;
}
}
break;
}
case 4:
{
// Get buffer position where upper-left pixel of the character will be drawn
uInt32* buffer = (uInt32*)getBasePtr(tx + bbx, ty + desc.ascent - bby - bbh);
for(int y = 0; y < bbh; y++, buffer += myPitch)
{
const uInt16 ptr = *tmp++;
uInt16 mask = 0x8000;
for(int x = 0; x < bbw; x++, mask >>= 1)
if(ptr & mask)
buffer[x] = (uInt32) myFB.myDefPalette[color];
}
break;
}
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::drawBitmap(uInt32* bitmap, uInt32 tx, uInt32 ty,
uInt32 color, uInt32 h)
{
SDL_Rect rect;
rect.y = ty + myYOffset;
rect.w = rect.h = 1;
for(uInt32 y = 0; y < h; y++)
{
rect.x = tx + myXOffset;
uInt32 mask = 0xF0000000;
for(uInt32 x = 0; x < 8; x++, mask >>= 4)
{
if(bitmap[y] & mask)
SDL_FillRect(mySurface, &rect, myFB.myDefPalette[color]);
rect.x++;
}
rect.y++;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::drawPixels(uInt32* data, uInt32 tx, uInt32 ty,
uInt32 numpixels)
{
SDL_Rect rect;
rect.x = tx + myXOffset;
rect.y = ty + myYOffset;
rect.w = rect.h = 1;
for(uInt32 x = 0; x < numpixels; ++x)
{
SDL_FillRect(mySurface, &rect, data[x]);
rect.x++;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::drawSurface(const FBSurface* surface, uInt32 tx, uInt32 ty)
{
const FBSurfaceSoft* s = (const FBSurfaceSoft*) surface;
SDL_Rect dstrect;
dstrect.x = tx + myXOffset;
dstrect.y = ty + myYOffset;
SDL_Rect srcrect;
srcrect.x = 0;
srcrect.y = 0;
srcrect.w = s->myWidth;
srcrect.h = s->myHeight;
SDL_BlitSurface(s->mySurface, &srcrect, mySurface, &dstrect);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
//cerr << " -> addDirtyRect: x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;
// Base surfaces use dirty-rectangle updates, since they can be quite
// large, and updating the entire surface each frame would be too slow
// Non-base surfaces are usually smaller, and can be updated entirely
if(myIsBaseSurface)
{
// Add a dirty rect to the UI rectangle list
SDL_Rect temp;
temp.x = x + myXOrig; temp.y = y + myYOrig; temp.w = w; temp.h = h;
myFB.myRectList->add(&temp);
}
else
{
SDL_Rect temp;
temp.x = myXOrig; temp.y = myYOrig; temp.w = myWidth; temp.h = myHeight;
myFB.myRectList->add(&temp);
// Indicate that at least one dirty rect has been added
// This is an optimization for the update() method
mySurfaceIsDirty = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::getPos(uInt32& x, uInt32& y) const
{
// Return the origin of the 'usable' area of a surface
if(myIsBaseSurface)
{
x = myXOffset;
y = myYOffset;
}
else
{
x = myXOrig;
y = myYOrig;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::setPos(uInt32 x, uInt32 y)
{
myXOrig = x;
myYOrig = y;
if(myIsBaseSurface)
{
myXOffset = myFB.imageRect().x();
myYOffset = myFB.imageRect().y();
}
else
{
myXOffset = myYOffset = 0;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::setWidth(uInt32 w)
{
myWidth = BSPF_min(w, (uInt32)mySurface->w);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::setHeight(uInt32 h)
{
myHeight = BSPF_min(h, (uInt32)mySurface->h);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::translateCoords(Int32& x, Int32& y) const
{
x -= myXOrig;
y -= myYOrig;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::update()
{
// Since this method is called each frame, we only blit the surfaces when
// absolutely necessary
if(mySurfaceIsDirty /* && !myIsBaseSurface */)
{
SDL_Rect srcrect;
srcrect.x = 0;
srcrect.y = 0;
srcrect.w = myWidth;
srcrect.h = myHeight;
SDL_Rect dstrect;
dstrect.x = myXOrig;
dstrect.y = myYOrig;
dstrect.w = myWidth;
dstrect.h = myHeight;
SDL_BlitSurface(mySurface, &srcrect, myFB.myScreen, &dstrect);
mySurfaceIsDirty = false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSoft::reload()
{
switch(mySurface->format->BytesPerPixel)
{
case 2: // 16-bit
myPitch = mySurface->pitch >> 1;
break;
case 3: // 24-bit
myPitch = mySurface->pitch;
break;
case 4: // 32-bit
myPitch = mySurface->pitch >> 2;
break;
}
}
+230
View File
@@ -0,0 +1,230 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef FRAMEBUFFER_SOFT_HXX
#define FRAMEBUFFER_SOFT_HXX
#include <SDL.h>
class OSystem;
class RectList;
#include "bspf.hxx"
#include "FrameBuffer.hxx"
/**
This class implements an SDL software framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class FrameBufferSoft : public FrameBuffer
{
friend class FBSurfaceSoft;
public:
/**
Creates a new software framebuffer
*/
FrameBufferSoft(OSystem* osystem);
/**
Destructor
*/
virtual ~FrameBufferSoft();
//////////////////////////////////////////////////////////////////////
// The following are derived from public methods in FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
Enable/disable phosphor effect.
*/
void enablePhosphor(bool enable, int blend);
/**
This method is called to retrieve the R/G/B data from the given pixel.
@param pixel The pixel containing R/G/B data
@param r The red component of the color
@param g The green component of the color
@param b The blue component of the color
*/
void getRGB(Uint32 pixel, Uint8* r, Uint8* g, Uint8* b) const
{ SDL_GetRGB(pixel, myScreen->format, r, g, b); }
/**
This method is called to map a given R/G/B triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b) const
{ return SDL_MapRGB(myScreen->format, r, g, b); }
/**
This method is called to query the type of the FrameBuffer.
*/
BufferType type() const { return kSoftBuffer; }
/**
This method is called to get the specified scanline data.
@param row The row we are looking for
@param data The actual pixel data (in bytes)
*/
void scanline(uInt32 row, uInt8* data) const;
protected:
//////////////////////////////////////////////////////////////////////
// The following are derived from protected methods in FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This method is called to initialize the video subsystem
with the given video mode. Normally, it will also call setVidMode().
@param mode The video mode to use
@return False on any errors, else true
*/
bool initSubsystem(VideoMode& mode);
/**
This method is called to change to the given video mode. If the mode
is successfully changed, 'mode' holds the actual dimensions used.
@param mode The video mode to use
@return False on any errors (in which case 'mode' is invalid), else true
*/
bool setVidMode(VideoMode& mode);
/**
This method is called to invalidate the contents of the entire
framebuffer (ie, mark the current content as invalid, and erase it on
the next drawing pass).
*/
void invalidate();
/**
This method is called to create a surface compatible with the one
currently in use, but having the given dimensions.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param useBase Use the base surface instead of creating a new one
*/
FBSurface* createSurface(int w, int h, bool useBase = false) const;
/**
This method should be called anytime the TIA needs to be redrawn
to the screen (full indicating that a full redraw is required).
*/
void drawTIA(bool full);
/**
This method is called after any drawing is done (per-frame).
*/
void postFrameUpdate();
/**
This method is called to provide information about the FrameBuffer.
*/
string about() const;
private:
int myZoomLevel;
int myBytesPerPixel;
int myBaseOffset;
int myPitch;
SDL_PixelFormat* myFormat;
enum RenderType {
kSoftZoom_16,
kSoftZoom_24,
kSoftZoom_32,
kPhosphor_16,
kPhosphor_24,
kPhosphor_32
};
RenderType myRenderType;
// Indicates if the TIA image has been modified
bool myTiaDirty;
// Indicates if we're in a purely UI mode
bool myInUIMode;
// Used in the dirty update of rectangles in non-TIA modes
RectList* myRectList;
};
/**
A surface suitable for software rendering mode.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class FBSurfaceSoft : public FBSurface
{
public:
FBSurfaceSoft(const FrameBufferSoft& buffer, SDL_Surface* surface,
uInt32 w, uInt32 h, bool isBase);
virtual ~FBSurfaceSoft();
void hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color);
void vLine(uInt32 x, uInt32 y, uInt32 y2, uInt32 color);
void fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color);
void drawChar(const GUI::Font& font, uInt8 c, uInt32 x, uInt32 y, uInt32 color);
void drawBitmap(uInt32* bitmap, uInt32 x, uInt32 y, uInt32 color, uInt32 h = 8);
void drawPixels(uInt32* data, uInt32 x, uInt32 y, uInt32 numpixels);
void drawSurface(const FBSurface* surface, uInt32 x, uInt32 y);
void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
void getPos(uInt32& x, uInt32& y) const;
void setPos(uInt32 x, uInt32 y);
uInt32 getWidth() const { return myWidth; }
uInt32 getHeight() const { return myHeight; }
void setWidth(uInt32 w);
void setHeight(uInt32 h);
void translateCoords(Int32& x, Int32& y) const;
void update();
void free() { } // Not required for software mode
void reload();
private:
void* getBasePtr(uInt32 x, uInt32 y) {
return static_cast<void *>(static_cast<uInt8*>(mySurface->pixels) +
(myYOffset + y) * mySurface->pitch + (myXOffset + x) *
mySurface->format->BytesPerPixel);
}
private:
const FrameBufferSoft& myFB;
SDL_Surface* mySurface;
uInt32 myWidth, myHeight;
bool myIsBaseSurface;
bool mySurfaceIsDirty;
int myPitch;
uInt32 myXOrig, myYOrig;
uInt32 myXOffset, myYOffset;
};
#endif
+261
View File
@@ -0,0 +1,261 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: MouseControl.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Console.hxx"
#include "Control.hxx"
#include "Props.hxx"
#include "MouseControl.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MouseControl::MouseControl(Console& console, const string& mode)
: myProps(console.properties()),
myLeftController(console.controller(Controller::Left)),
myRightController(console.controller(Controller::Right)),
myCurrentModeNum(0)
{
if(BSPF_equalsIgnoreCase(mode, "none"))
{
myModeList.push_back(MouseMode("Mouse input is disabled"));
return;
}
else if(!BSPF_equalsIgnoreCase(mode, "auto") && mode.length() == 2 &&
mode[0] >= '0' && mode[0] <= '8' &&
mode[1] >= '0' && mode[1] <= '8')
{
Axis xaxis = (Axis) ((int)mode[0] - '0');
Axis yaxis = (Axis) ((int)mode[1] - '0');
ostringstream msg;
msg << "Mouse X-axis is ";
Controller::Type xtype = Controller::Joystick, ytype = Controller::Joystick;
int xid = -1, yid = -1;
switch(xaxis)
{
case NoControl:
msg << "not used";
break;
case Paddle0:
xtype = Controller::Paddles;
xid = 0;
msg << "Paddle 0";
break;
case Paddle1:
xtype = Controller::Paddles;
xid = 1;
msg << "Paddle 1";
break;
case Paddle2:
xtype = Controller::Paddles;
xid = 2;
msg << "Paddle 2";
break;
case Paddle3:
xtype = Controller::Paddles;
xid = 3;
msg << "Paddle 3";
break;
case Driving0:
xtype = Controller::Driving;
xid = 0;
msg << "Driving 0";
break;
case Driving1:
xtype = Controller::Driving;
xid = 1;
msg << "Driving 1";
break;
case MindLink0:
xtype = Controller::MindLink;
xid = 0;
msg << "MindLink 0";
break;
case MindLink1:
xtype = Controller::MindLink;
xid = 1;
msg << "MindLink 1";
break;
default: break;
}
msg << ", Y-axis is ";
switch(yaxis)
{
case NoControl:
msg << "not used";
break;
case Paddle0:
ytype = Controller::Paddles;
yid = 0;
msg << "Paddle 0";
break;
case Paddle1:
ytype = Controller::Paddles;
yid = 1;
msg << "Paddle 1";
break;
case Paddle2:
ytype = Controller::Paddles;
yid = 2;
msg << "Paddle 2";
break;
case Paddle3:
ytype = Controller::Paddles;
yid = 3;
msg << "Paddle 3";
break;
case Driving0:
ytype = Controller::Driving;
yid = 0;
msg << "Driving 0";
break;
case Driving1:
ytype = Controller::Driving;
yid = 1;
msg << "Driving 1";
break;
case MindLink0:
ytype = Controller::MindLink;
yid = 0;
msg << "MindLink 0";
break;
case MindLink1:
ytype = Controller::MindLink;
yid = 1;
msg << "MindLink 1";
break;
default: break;
}
myModeList.push_back(MouseMode(xtype, xid, ytype, yid, msg.str()));
}
// Now consider the possible modes for the mouse based on the left
// and right controllers
bool noswap = BSPF_equalsIgnoreCase(myProps.get(Console_SwapPorts), "NO");
if(noswap)
{
addLeftControllerModes(noswap);
addRightControllerModes(noswap);
}
else
{
addRightControllerModes(noswap);
addLeftControllerModes(noswap);
}
// If the mouse isn't used at all, we still need one item in the list
if(myModeList.size() == 0)
myModeList.push_back(MouseMode("Mouse not used for current controllers"));
#if 0
for(unsigned int i = 0; i < myModeList.size(); ++i)
cerr << myModeList[i] << endl;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MouseControl::~MouseControl()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& MouseControl::next()
{
const MouseMode& mode = myModeList[myCurrentModeNum];
myCurrentModeNum = (myCurrentModeNum + 1) % myModeList.size();
myLeftController.setMouseControl(mode.xtype, mode.xid, mode.ytype, mode.yid);
myRightController.setMouseControl(mode.xtype, mode.xid, mode.ytype, mode.yid);
return mode.message;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MouseControl::addLeftControllerModes(bool noswap)
{
if(controllerSupportsMouse(myLeftController))
{
if(myLeftController.type() == Controller::Paddles)
{
if(noswap) addPaddleModes(0, 1, 0, 1);
else addPaddleModes(2, 3, 0, 1);
}
else
{
ostringstream msg;
msg << "Mouse is left " << myLeftController.name() << " controller";
Controller::Type type = myLeftController.type();
int id = noswap ? 0 : 1;
myModeList.push_back(MouseMode(type, id, type, id, msg.str()));
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MouseControl::addRightControllerModes(bool noswap)
{
if(controllerSupportsMouse(myRightController))
{
if(myRightController.type() == Controller::Paddles)
{
if(noswap) addPaddleModes(2, 3, 2, 3);
else addPaddleModes(0, 1, 2, 3);
}
else
{
ostringstream msg;
msg << "Mouse is right " << myRightController.name() << " controller";
Controller::Type type = myRightController.type();
int id = noswap ? 1 : 0;
myModeList.push_back(MouseMode(type, id, type, id, msg.str()));
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MouseControl::addPaddleModes(int lport, int rport, int lname, int rname)
{
Controller::Type type = Controller::Paddles;
ostringstream msg;
msg << "Mouse is Paddle " << lname << " controller";
MouseMode mode0(type, lport, type, lport, msg.str());
msg.str("");
msg << "Mouse is Paddle " << rname << " controller";
MouseMode mode1(type, rport, type, rport, msg.str());
if(BSPF_equalsIgnoreCase(myProps.get(Controller_SwapPaddles), "NO"))
{
myModeList.push_back(mode0);
myModeList.push_back(mode1);
}
else
{
myModeList.push_back(mode1);
myModeList.push_back(mode0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool MouseControl::controllerSupportsMouse(Controller& controller)
{
// Test whether the controller uses the mouse at all
// We can pass in dummy values here, since the controllers will be
// initialized by a call to next() once the system is up and running
return controller.setMouseControl(
Controller::Joystick, -1, Controller::Joystick, -1);
}
+120
View File
@@ -0,0 +1,120 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: MouseControl.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef MOUSE_CONTROL_HXX
#define MOUSE_CONTROL_HXX
class Console;
class Properties;
#include "bspf.hxx"
#include "Array.hxx"
#include "Control.hxx"
/**
The mouse can control various virtual 'controllers' in many different
ways. In 'auto' mode, the entire mouse (both axes and buttons) are used
as one controller. In per-ROM axis mode, each axis/button may control
separate controllers. As well, we'd like to switch dynamically between
each of these modes at runtime.
This class encapsulates all required info to implement this functionality.
@author Stephen Anthony
*/
class MouseControl
{
public:
/**
Enumeration of mouse axis control types
*/
enum Axis
{
Paddle0 = 0, Paddle1, Paddle2, Paddle3,
Driving0, Driving1, MindLink0, MindLink1,
NoControl
};
public:
/**
Create a new MouseControl object
@param console The console in use by the system
@param mode Contains information about how to use the mouse axes/buttons
*/
MouseControl(Console& console, const string& mode);
/**
Destructor
*/
virtual ~MouseControl();
public:
/**
Cycle through each available mouse control mode
@return A message explaining the current mouse mode
*/
const string& next();
private:
void addLeftControllerModes(bool noswap);
void addRightControllerModes(bool noswap);
void addPaddleModes(int lport, int rport, int lname, int rname);
bool controllerSupportsMouse(Controller& controller);
private:
const Properties& myProps;
Controller& myLeftController;
Controller& myRightController;
struct MouseMode {
Controller::Type xtype, ytype;
int xid, yid;
string message;
MouseMode(const string& msg = "")
: xtype(Controller::Joystick),
ytype(Controller::Joystick),
xid(-1),
yid(-1),
message(msg) { }
MouseMode(Controller::Type xtype, int xid,
Controller::Type ytype, int yid,
const string& msg)
: xtype(xtype),
ytype(ytype),
xid(xid),
yid(yid),
message(msg) { }
friend ostream& operator<<(ostream& os, const MouseMode& mm)
{
os << "xtype=" << mm.xtype << ", xid=" << mm.xid
<< ", ytype=" << mm.ytype << ", yid=" << mm.yid
<< ", msg=" << mm.message;
return os;
}
};
int myCurrentModeNum;
Common::Array<MouseMode> myModeList;
};
#endif
+446
View File
@@ -0,0 +1,446 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: PNGLibrary.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <zlib.h>
#include <fstream>
#include <cstring>
#include <sstream>
#include <cmath>
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "Props.hxx"
#include "TIA.hxx"
#include "Version.hxx"
#include "PNGLibrary.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::PNGLibrary()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::~PNGLibrary()
{
delete[] ReadInfo.buffer;
delete[] ReadInfo.line;
delete[] ReadInfo.row_pointers;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::loadImage(const string& filename,
const FrameBuffer& fb, FBSurface& surface)
{
#define readImageERROR(s) { err_message = s; goto done; }
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_uint_32 iwidth, iheight;
int bit_depth, color_type, interlace_type;
const char* err_message = NULL;
ifstream in(filename.c_str(), ios_base::binary);
if(!in.is_open())
readImageERROR("No image found");
// Create the PNG loading context structure
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
png_user_error, png_user_warn);
if(png_ptr == NULL)
readImageERROR("Couldn't allocate memory for PNG file");
// Allocate/initialize the memory for image information. REQUIRED.
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL)
readImageERROR("Couldn't create image information for PNG file");
// Set up the input control
png_set_read_fn(png_ptr, &in, png_read_data);
// Read PNG header info
png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &iwidth, &iheight, &bit_depth,
&color_type, &interlace_type, NULL, NULL);
// Tell libpng to strip 16 bit/color files down to 8 bits/color
png_set_strip_16(png_ptr);
// Extract multiple pixels with bit depths of 1, 2, and 4 from a single
// byte into separate bytes (useful for paletted and grayscale images).
png_set_packing(png_ptr);
// Only normal RBG(A) images are supported (without the alpha channel)
if(color_type == PNG_COLOR_TYPE_RGBA)
{
png_set_strip_alpha(png_ptr);
}
else if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
readImageERROR("Greyscale PNG images not supported");
}
else if(color_type == PNG_COLOR_TYPE_PALETTE)
{
readImageERROR("Paletted PNG images not supported");
}
else if(color_type != PNG_COLOR_TYPE_RGB)
{
readImageERROR("Unknown format in PNG image");
}
// Create/initialize storage area for the current image
if(!allocateStorage(iwidth, iheight))
readImageERROR("Not enough memory to read PNG file");
// The PNG read function expects an array of rows, not a single 1-D array
for(uInt32 irow = 0, offset = 0; irow < ReadInfo.height; ++irow, offset += ReadInfo.pitch)
ReadInfo.row_pointers[irow] = (png_bytep) (uInt8*)ReadInfo.buffer + offset;
// Read the entire image in one go
png_read_image(png_ptr, ReadInfo.row_pointers);
// We're finished reading
png_read_end(png_ptr, info_ptr);
// Scale image to surface dimensions
scaleImagetoSurface(fb, surface);
// Cleanup
done:
if(png_ptr)
png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : (png_infopp)0, (png_infopp)0);
if(err_message)
throw err_message;
else
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PNGLibrary::saveImage(const string& filename,
const FrameBuffer& framebuffer,
const Properties& props)
{
ofstream out(filename.c_str(), ios_base::binary);
if(!out.is_open())
return "ERROR: Couldn't create snapshot file";
// Get actual image dimensions. which are not always the same
// as the framebuffer dimensions
const GUI::Rect& image = framebuffer.imageRect();
uInt32 width = image.width(), height = image.height(),
pitch = width * 3;
uInt8* buffer = new uInt8[(pitch + 1) * height];
// Fill the buffer with scanline data
uInt8* buf_ptr = buffer;
for(uInt32 row = 0; row < height; row++)
{
*buf_ptr++ = 0; // first byte of row is filter type
framebuffer.scanline(row, buf_ptr); // get another scanline
buf_ptr += pitch; // add pitch
}
return saveBufferToPNG(out, buffer, width, height,
props, framebuffer.effectsInfo());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PNGLibrary::saveImage(const string& filename,
const FrameBuffer& framebuffer, const TIA& tia,
const Properties& props)
{
ofstream out(filename.c_str(), ios_base::binary);
if(!out.is_open())
return "ERROR: Couldn't create snapshot file";
uInt32 width = tia.width(), height = tia.height();
uInt8* buffer = new uInt8[(width*3*2 + 1) * height];
// Fill the buffer with pixels from the mediasrc
uInt8 r, g, b;
uInt8* buf_ptr = buffer;
for(uInt32 y = 0; y < height; ++y)
{
*buf_ptr++ = 0; // first byte of row is filter type
for(uInt32 x = 0; x < width; ++x)
{
uInt32 pixel = framebuffer.tiaPixel(y*width+x);
framebuffer.getRGB(pixel, &r, &g, &b);
*buf_ptr++ = r;
*buf_ptr++ = g;
*buf_ptr++ = b;
*buf_ptr++ = r;
*buf_ptr++ = g;
*buf_ptr++ = b;
}
}
return saveBufferToPNG(out, buffer, width << 1, height,
props, framebuffer.effectsInfo());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PNGLibrary::saveBufferToPNG(ofstream& out, uInt8* buffer,
uInt32 width, uInt32 height,
const Properties& props,
const string& effectsInfo)
{
uInt8* compmem = (uInt8*) NULL;
try
{
// PNG file header
uInt8 header[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
out.write((const char*)header, 8);
// PNG IHDR
uInt8 ihdr[13];
ihdr[0] = width >> 24; // width
ihdr[1] = width >> 16;
ihdr[2] = width >> 8;
ihdr[3] = width & 0xFF;
ihdr[4] = height >> 24; // height
ihdr[5] = height >> 16;
ihdr[6] = height >> 8;
ihdr[7] = height & 0xFF;
ihdr[8] = 8; // 8 bits per sample (24 bits per pixel)
ihdr[9] = 2; // PNG_COLOR_TYPE_RGB
ihdr[10] = 0; // PNG_COMPRESSION_TYPE_DEFAULT
ihdr[11] = 0; // PNG_FILTER_TYPE_DEFAULT
ihdr[12] = 0; // PNG_INTERLACE_NONE
writePNGChunk(out, "IHDR", ihdr, 13);
// Compress the data with zlib
uLongf compmemsize = (uLongf)((height * (width + 1) * 3 * 1.001 + 1) + 12);
compmem = new uInt8[compmemsize];
if(compmem == NULL ||
(compress(compmem, &compmemsize, buffer, height * (width * 3 + 1)) != Z_OK))
throw "ERROR: Couldn't compress PNG";
// Write the compressed framebuffer data
writePNGChunk(out, "IDAT", compmem, compmemsize);
// Add some info about this snapshot
ostringstream text;
text << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
<< BSPF_ARCH << "]";
writePNGText(out, "Software", text.str());
writePNGText(out, "ROM Name", props.get(Cartridge_Name));
writePNGText(out, "ROM MD5", props.get(Cartridge_MD5));
writePNGText(out, "TV Effects", effectsInfo);
// Finish up
writePNGChunk(out, "IEND", 0, 0);
// Clean up
if(buffer) delete[] buffer;
if(compmem) delete[] compmem;
out.close();
return "Snapshot saved";
}
catch(const char* msg)
{
if(buffer) delete[] buffer;
if(compmem) delete[] compmem;
out.close();
return msg;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h)
{
// Create space for the entire image (3 bytes per pixel in RGB format)
uInt32 req_buffer_size = w * h * 3;
if(req_buffer_size > ReadInfo.buffer_size)
{
delete[] ReadInfo.buffer;
ReadInfo.buffer = new uInt8[req_buffer_size];
if(ReadInfo.buffer == NULL)
return false;
ReadInfo.buffer_size = req_buffer_size;
}
uInt32 req_line_size = w * 3;
if(req_line_size > ReadInfo.line_size)
{
delete[] ReadInfo.line;
ReadInfo.line = new uInt32[req_line_size];
if(ReadInfo.line == NULL)
return false;
ReadInfo.line_size = req_line_size;
}
uInt32 req_row_size = h;
if(req_row_size > ReadInfo.row_size)
{
delete[] ReadInfo.row_pointers;
ReadInfo.row_pointers = new png_bytep[req_row_size];
if(ReadInfo.row_pointers == NULL)
return false;
ReadInfo.row_size = req_row_size;
}
ReadInfo.width = w;
ReadInfo.height = h;
ReadInfo.pitch = w * 3;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface)
{
// Figure out the original zoom level of the snapshot
// All snapshots generated by Stella are at most some multiple of 320
// pixels wide
// The only complication is when the aspect ratio is changed, the width
// can range from 256 (80%) to 320 (100%)
// The following calculation will work up to approx. 16x zoom level,
// but since Stella only generates snapshots at up to 10x, we should
// be fine for a while ...
uInt32 izoom = uInt32(ceil(ReadInfo.width/320.0)),
szoom = surface.getWidth()/320;
uInt32 sw = ReadInfo.width / izoom * szoom,
sh = ReadInfo.height / izoom * szoom;
sw = BSPF_min(sw, surface.getWidth());
sh = BSPF_min(sh, surface.getHeight());
surface.setWidth(sw);
surface.setHeight(sh);
// Decompress the image, and scale it correctly
uInt32 buf_offset = ReadInfo.pitch * izoom;
uInt32 i_offset = 3 * izoom;
// We can only scan at most the height of the image to the constraints of
// the surface height (some multiple of 256)
uInt32 iheight = BSPF_min((uInt32)ReadInfo.height, izoom * 256);
// Grab each non-duplicate row of data from the image
uInt8* buffer = ReadInfo.buffer;
for(uInt32 irow = 0, srow = 0; irow < iheight; irow += izoom, buffer += buf_offset)
{
// Scale the image data into the temporary line buffer
uInt8* i_ptr = buffer;
uInt32* l_ptr = ReadInfo.line;
for(uInt32 icol = 0; icol < ReadInfo.width; icol += izoom, i_ptr += i_offset)
{
uInt32 pixel = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
uInt32 xstride = szoom;
while(xstride--)
*l_ptr++ = pixel;
}
// Then fill the surface with those bytes
uInt32 ystride = szoom;
while(ystride--)
surface.drawPixels(ReadInfo.line, 0, srow++, sw);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::writePNGChunk(ofstream& out, const char* type,
uInt8* data, int size)
{
// Stuff the length/type into the buffer
uInt8 temp[8];
temp[0] = size >> 24;
temp[1] = size >> 16;
temp[2] = size >> 8;
temp[3] = size;
temp[4] = type[0];
temp[5] = type[1];
temp[6] = type[2];
temp[7] = type[3];
// Write the header
out.write((const char*)temp, 8);
// Append the actual data
uInt32 crc = crc32(0, temp + 4, 4);
if(size > 0)
{
out.write((const char*)data, size);
crc = crc32(crc, data, size);
}
// Write the CRC
temp[0] = crc >> 24;
temp[1] = crc >> 16;
temp[2] = crc >> 8;
temp[3] = crc;
out.write((const char*)temp, 4);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::writePNGText(ofstream& out, const string& key, const string& text)
{
int length = key.length() + 1 + text.length() + 1;
uInt8* data = new uInt8[length];
strcpy((char*)data, key.c_str());
strcpy((char*)data + key.length() + 1, text.c_str());
writePNGChunk(out, "tEXt", data, length-1);
delete[] data;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size)
{
ifstream* stream = (ifstream *) png_get_io_ptr(ctx);
stream->read((char *)area, size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
{
ofstream* stream = (ofstream *) png_get_io_ptr(ctx);
stream->write((const char *)area, size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_io_flush(png_structp ctx)
{
ofstream* stream = (ofstream *) png_get_io_ptr(ctx);
stream->flush();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_user_warn(png_structp ctx, png_const_charp str)
{
const string& msg = string("PNGLibrary warning: ") + str;
throw msg.c_str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_user_error(png_structp ctx, png_const_charp str)
{
const string& msg = string("PNGLibrary error: ") + str;
throw msg.c_str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::ReadInfoType PNGLibrary::ReadInfo = {
NULL, NULL, 0, 0, 0, NULL, 0, 0, 0
};
+129
View File
@@ -0,0 +1,129 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: PNGLibrary.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef PNGLIBRARY_HXX
#define PNGLIBRARY_HXX
#include <png.h>
class FrameBuffer;
class FBSurface;
class Properties;
class TIA;
#include <fstream>
#include "bspf.hxx"
/**
This class implements a thin wrapper around the libpng library, and
abstracts all the irrelevant details other loading and saving an
actual image.
@author Stephen Anthony
*/
class PNGLibrary
{
public:
PNGLibrary();
virtual ~PNGLibrary();
/**
Read a PNG image from the specified file into a FBSurface structure,
scaling the image to the surface bounds.
@param filename The filename to load the PNG image
@param fb The main framebuffer of the application
@param surface The FBSurface into which to place the PNG data
@return On success, the FBSurface containing image data and a
result of true, otherwise a const char* exception is thrown
containing a more detailed error message.
*/
bool loadImage(const string& filename, const FrameBuffer& fb, FBSurface& surface);
/**
Save the current TIA image to a PNG file using data from the Framebuffer.
Any postprocessing/filtering will be included.
@param filename The filename to save the PNG image
@param framebuffer The framebuffer containing the image data
@param props The properties object containing info about the ROM
*/
string saveImage(const string& filename, const FrameBuffer& framebuffer,
const Properties& props);
/**
Save the current TIA image to a PNG file using data directly from
the TIA framebuffer. No filtering or scaling will be included.
@param filename The filename to save the PNG image
@param framebuffer The framebuffer containing the image data
@param mediasrc Source of the raw TIA data
@param props The properties object containing info about the ROM
*/
string saveImage(const string& filename, const FrameBuffer& framebuffer,
const TIA& tia, const Properties& props);
private:
// The following data remains between invocations of allocateStorage,
// and is only changed when absolutely necessary.
typedef struct {
uInt8* buffer;
png_bytep* row_pointers;
png_uint_32 width, height, pitch;
uInt32* line;
uInt32 buffer_size, line_size, row_size;
} ReadInfoType;
static ReadInfoType ReadInfo;
/**
Allocate memory for PNG read operations. This is used to provide a
basic memory manager, so that we don't constantly allocate and deallocate
memory for each image loaded.
The method fills the 'ReadInfo' struct with valid memory locations
dependent on the given dimensions. If memory has been previously
allocated and it can accommodate the given dimensions, it is used directly.
*/
bool allocateStorage(png_uint_32 iwidth, png_uint_32 iheight);
/**
Scale the PNG data from 'ReadInfo' into the FBSurface. For now, scaling
is done on integer boundaries only (ie, 1x, 2x, etc up or down).
@param fb The main framebuffer of the application
@param surface The FBSurface into which to place the PNG data
*/
void scaleImagetoSurface(const FrameBuffer& fb, FBSurface& surface);
string saveBufferToPNG(ofstream& out, uInt8* buffer,
uInt32 width, uInt32 height,
const Properties& props,
const string& effectsInfo);
void writePNGChunk(ofstream& out, const char* type, uInt8* data, int size);
void writePNGText(ofstream& out, const string& key, const string& text);
static void png_read_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_io_flush(png_structp ctx);
static void png_user_warn(png_structp ctx, png_const_charp str);
static void png_user_error(png_structp ctx, png_const_charp str);
};
#endif
+96
View File
@@ -0,0 +1,96 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RectList.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <SDL.h>
#include "bspf.hxx"
#include "RectList.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RectList::RectList(Uint32 size)
{
currentSize = size;
currentRect = 0;
rectArray = new SDL_Rect[currentSize];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RectList::~RectList()
{
delete[] rectArray;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::add(SDL_Rect* newRect)
{
if(currentRect >= currentSize)
{
currentSize = currentSize * 2;
SDL_Rect *temp = new SDL_Rect[currentSize];
for(Uint32 i = 0; i < currentRect; ++i)
temp[i] = rectArray[i];
delete[] rectArray;
rectArray = temp;
}
rectArray[currentRect].x = newRect->x;
rectArray[currentRect].y = newRect->y;
rectArray[currentRect].w = newRect->w;
rectArray[currentRect].h = newRect->h;
++currentRect;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SDL_Rect* RectList::rects()
{
return rectArray;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Uint32 RectList::numRects()
{
return currentRect;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::start()
{
currentRect = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::print(int boundWidth, int boundHeight)
{
cerr << "RectList: rects = " << numRects() << endl;
for(Uint32 i = 0; i < currentRect; ++i)
{
cerr << "Rect " << i << endl
<< " x = " << rectArray[i].x << endl
<< " y = " << rectArray[i].y << endl
<< " w = " << rectArray[i].w << endl
<< " h = " << rectArray[i].h << endl;
if((rectArray[i].x + rectArray[i].w) > boundWidth ||
(rectArray[i].y + rectArray[i].h) > boundHeight)
cerr << " TOO LARGE" << endl;
cerr << endl;
}
}
+44
View File
@@ -0,0 +1,44 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RectList.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef RECTLIST_HXX
#define RECTLIST_HXX
#include <SDL.h>
class RectList
{
public:
RectList(Uint32 size = 256);
~RectList();
void add(SDL_Rect* rect);
SDL_Rect* rects();
Uint32 numRects();
void start();
void print(int boundWidth, int boundHeight);
private:
Uint32 currentSize, currentRect;
SDL_Rect* rectArray;
};
#endif
@@ -8,13 +8,13 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2011 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SharedPtr.hxx 2199 2011-01-01 16:04:32Z stephena $
// $Id: SharedPtr.hxx 2838 2014-01-17 23:34:03Z stephena $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
+184
View File
@@ -0,0 +1,184 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundNull.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef SOUND_NULL_HXX
#define SOUND_NULL_HXX
class OSystem;
#include "bspf.hxx"
#include "Sound.hxx"
#include "OSystem.hxx"
/**
This class implements a Null sound object, where-by sound generation
is completely disabled.
@author Stephen Anthony
@version $Id: SoundNull.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class SoundNull : public Sound
{
public:
/**
Create a new sound object. The init method must be invoked before
using the object.
*/
SoundNull(OSystem* osystem) : Sound(osystem)
{
myOSystem->logMessage("Sound disabled.\n", 1);
}
/**
Destructor
*/
virtual ~SoundNull();
public:
/**
Enables/disables the sound subsystem.
@param enable Either true or false, to enable or disable the sound system
@return Whether the sound system was enabled or disabled
*/
void setEnabled(bool enable) { }
/**
The system cycle counter is being adjusting by the specified amount. Any
members using the system cycle counter should be adjusted as needed.
@param amount The amount the cycle counter is being adjusted by
*/
void adjustCycleCounter(Int32 amount) { }
/**
Sets the number of channels (mono or stereo sound).
@param channels The number of channels
*/
void setChannels(uInt32 channels) { }
/**
Sets the display framerate. Sound generation for NTSC and PAL games
depends on the framerate, so we need to set it here.
@param framerate The base framerate depending on NTSC or PAL ROM
*/
void setFrameRate(float framerate) { }
/**
Initializes the sound device. This must be called before any
calls are made to derived methods.
*/
void open() { }
/**
Should be called to close the sound device. Once called the sound
device can be started again using the initialize method.
*/
void close() { }
/**
Set the mute state of the sound object. While muted no sound is played.
@param state Mutes sound if true, unmute if false
*/
void mute(bool state) { }
/**
Reset the sound device.
*/
void reset() { }
/**
Sets the sound register to a given value.
@param addr The register address
@param value The value to save into the register
@param cycle The system cycle at which the register is being updated
*/
void set(uInt16 addr, uInt8 value, Int32 cycle) { }
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. Values outside
this range indicate that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
*/
void setVolume(Int32 percent) { }
/**
Adjusts the volume of the sound device based on the given direction.
@param direction Increase or decrease the current volume by a predefined
amount based on the direction (1 = increase, -1 =decrease)
*/
void adjustVolume(Int8 direction) { }
public:
/**
Saves the current state of this device to the given Serializer.
@param out The serializer device to save to.
@return The result of the save. True on success, false on failure.
*/
bool save(Serializer& out) const
{
out.putString("TIASound");
for(int = 0; i < 6; ++i)
out.putByte(0);
// myLastRegisterSetCycle
out.putInt(0);
return true;
}
/**
Loads the current state of this device from the given Serializer.
@param in The Serializer device to load from.
@return The result of the load. True on success, false on failure.
*/
bool load(Serializer& in)
{
if(in.getString() != "TIASound")
return false;
// Read sound registers and discard
for(int = 0; i < 6; ++i)
in.getByte();
// myLastRegisterSetCycle
in.getInt();
return true;
}
/**
Get a descriptor for this console class (used in error checking).
@return The name of the object
*/
string name() const { return "TIASound"; }
};
#endif
+613
View File
@@ -0,0 +1,613 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundSDL.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifdef SOUND_SUPPORT
#include <sstream>
#include <cassert>
#include <cmath>
//#include <SDL.h>
#include "TIASnd.hxx"
#include "FrameBuffer.hxx"
#include "Settings.hxx"
#include "System.hxx"
#include "OSystem.hxx"
#include "Console.hxx"
#include "SoundSDL.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SoundSDL(OSystem* osystem)
: Sound(osystem),
myIsEnabled(false),
myIsInitializedFlag(false),
myLastRegisterSetCycle(0),
myNumChannels(0),
myFragmentSizeLogBase2(0),
myIsMuted(true),
myVolume(100)
{
myOSystem->logMessage("SoundSDL::SoundSDL started ...", 2);
// The sound system is opened only once per program run, to eliminate
// issues with opening and closing it multiple times
// This fixes a bug most prevalent with ATI video cards in Windows,
// whereby sound stopped working after the first video change
SDL_AudioSpec desired;
//desired.freq = myOSystem->settings().getInt("freq");
desired.freq = 31400;
//desired.format = AUDIO_S16SYS;
desired.channels = 2;
//desired.samples = myOSystem->settings().getInt("fragsize");
desired.samples = 512;
desired.callback = callback;
desired.userdata = (void*)this;
//
// ostringstream buf;
// if(SDL_OpenAudio(&desired, &myHardwareSpec) < 0)
// {
// buf << "WARNING: Couldn't open SDL audio system! " << endl
// << " " << SDL_GetError() << endl;
// myOSystem->logMessage(buf.str(), 0);
// return;
// }
// Make sure the sample buffer isn't to big (if it is the sound code
// will not work so we'll need to disable the audio support)
// if(((float)myHardwareSpec.samples / (float)myHardwareSpec.freq) >= 0.25)
// {
// buf << "WARNING: Sound device doesn't support realtime audio! Make "
// << "sure a sound" << endl
// << " server isn't running. Audio is disabled." << endl;
// myOSystem->logMessage(buf.str(), 0);
//
// SDL_CloseAudio();
// return;
// }
// Pre-compute fragment-related variables as much as possible
// myFragmentSizeLogBase2 = log((double)512) / log(2.0);
// myFragmentSizeLogDiv1 = myFragmentSizeLogBase2 / 60.0;
// myFragmentSizeLogDiv2 = (myFragmentSizeLogBase2 - 1) / 60.0;
myIsInitializedFlag = true;
//SDL_PauseAudio(1);
myOSystem->logMessage("SoundSDL::SoundSDL initialized", 2);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::~SoundSDL()
{
// Close the SDL audio system if it's initialized
if(myIsInitializedFlag)
{
//SDL_CloseAudio();
myIsEnabled = myIsInitializedFlag = false;
}
myOSystem->logMessage("SoundSDL destroyed", 2);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setEnabled(bool state)
{
myOSystem->settings().setValue("sound", state);
myOSystem->logMessage(state ? "SoundSDL::setEnabled(true)" :
"SoundSDL::setEnabled(false)", 2);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::open()
{
myOSystem->logMessage("SoundSDL::open started ...", 2);
myIsEnabled = false;
mute(true);
if(!myIsInitializedFlag || !myOSystem->settings().getBool("sound"))
{
myOSystem->logMessage("Sound disabled\n", 1);
return;
}
// Now initialize the TIASound object which will actually generate sound
myTIASound.outputFrequency(31400);
const string& chanResult =
myTIASound.channels(2, myNumChannels == 2);
// Adjust volume to that defined in settings
myVolume = myOSystem->settings().getInt("volume");
setVolume(myVolume);
// Show some info
ostringstream buf;
buf << "Sound enabled:" << endl
<< " Volume: " << (int)myVolume << endl
<< " Frag size: " << (int)512 << endl
<< " Frequency: " << (int)31400 << endl
<< " Channels: " << (int)2
<< " (" << chanResult << ")" << endl
<< endl;
myOSystem->logMessage(buf.str(), 1);
// And start the SDL sound subsystem ...
myIsEnabled = true;
mute(false);
myOSystem->logMessage("SoundSDL::open finished", 2);
// Pre-compute fragment-related variables as much as possible
myFragmentSizeLogBase2 = log((double)512) / log(2.0);
myFragmentSizeLogDiv1 = myFragmentSizeLogBase2 / 60.0;
myFragmentSizeLogDiv2 = (myFragmentSizeLogBase2 - 1) / 60.0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::close()
{
if(myIsInitializedFlag)
{
myIsEnabled = false;
//SDL_PauseAudio(1);
myLastRegisterSetCycle = 0;
myTIASound.reset();
myRegWriteQueue.clear();
myOSystem->logMessage("SoundSDL::close", 2);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::mute(bool state)
{
if(myIsInitializedFlag)
{
myIsMuted = state;
//SDL_PauseAudio(myIsMuted ? 1 : 0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::reset()
{
if(myIsInitializedFlag)
{
//SDL_PauseAudio(1);
myLastRegisterSetCycle = 0;
myTIASound.reset();
myRegWriteQueue.clear();
mute(myIsMuted);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setVolume(Int32 percent)
{
if(myIsInitializedFlag && (percent >= 0) && (percent <= 100))
{
myOSystem->settings().setValue("volume", percent);
//SDL_LockAudio();
myVolume = percent;
myTIASound.volume(percent);
//SDL_UnlockAudio();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::adjustVolume(Int8 direction)
{
ostringstream strval;
string message;
Int32 percent = myVolume;
if(direction == -1)
percent -= 2;
else if(direction == 1)
percent += 2;
if((percent < 0) || (percent > 100))
return;
setVolume(percent);
// Now show an onscreen message
strval << percent;
message = "Volume set to ";
message += strval.str();
myOSystem->frameBuffer().showMessage(message);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::adjustCycleCounter(Int32 amount)
{
myLastRegisterSetCycle += amount;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setChannels(uInt32 channels)
{
if(channels == 1 || channels == 2)
myNumChannels = channels;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setFrameRate(float framerate)
{
// Recalculate since frame rate has changed
// FIXME - should we clear out the queue or adjust the values in it?
myFragmentSizeLogDiv1 = myFragmentSizeLogBase2 / framerate;
myFragmentSizeLogDiv2 = (myFragmentSizeLogBase2 - 1) / framerate;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::set(uInt16 addr, uInt8 value, Int32 cycle)
{
//SDL_LockAudio();
// First, calculate how many seconds would have past since the last
// register write on a real 2600
double delta = (((double)(cycle - myLastRegisterSetCycle)) /
(1193191.66666667));
// Now, adjust the time based on the frame rate the user has selected. For
// the sound to "scale" correctly, we have to know the games real frame
// rate (e.g., 50 or 60) and the currently emulated frame rate. We use these
// values to "scale" the time before the register change occurs.
RegWrite info;
info.addr = addr;
info.value = value;
info.delta = delta;
myRegWriteQueue.enqueue(info);
// Update last cycle counter to the current cycle
myLastRegisterSetCycle = cycle;
//SDL_UnlockAudio();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::processFragment(Int16* stream, uInt32 length)
{
// uInt32 channels = 1;
// length = length / channels;
//
// // If there are excessive items on the queue then we'll remove some
// if(myRegWriteQueue.duration() > myFragmentSizeLogDiv1)
// {
// double removed = 0.0;
// while(removed < myFragmentSizeLogDiv2)
// {
// RegWrite& info = myRegWriteQueue.front();
// removed += info.delta;
// myTIASound.set(info.addr, info.value);
// myRegWriteQueue.dequeue();
// }
// }
const uInt32 channels = 2;
// If there are excessive items on the queue then we'll remove some
//logMsg("sound duration %f", myRegWriteQueue.duration());
double streamLengthInSecs = (double)length/(double)31400;
double excessStreamSecs = myRegWriteQueue.duration() - streamLengthInSecs;
if(excessStreamSecs > 0.0)
{
double removed = 0.0;
while(removed < excessStreamSecs)
{
RegWrite& info = myRegWriteQueue.front();
removed += info.delta;
myTIASound.set(info.addr, info.value);
myRegWriteQueue.dequeue();
}
}
double position = 0.0;
double remaining = length;
while(remaining > 0.0)
{
if(myRegWriteQueue.size() == 0)
{
// There are no more pending TIA sound register updates so we'll
// use the current settings to finish filling the sound fragment
myTIASound.process(stream + ((uInt32)position * channels),
length - (uInt32)position);
// Since we had to fill the fragment we'll reset the cycle counter
// to zero. NOTE: This isn't 100% correct, however, it'll do for
// now. We should really remember the overrun and remove it from
// the delta of the next write.
myLastRegisterSetCycle = 0;
break;
}
else
{
// There are pending TIA sound register updates so we need to
// update the sound buffer to the point of the next register update
RegWrite& info = myRegWriteQueue.front();
// How long will the remaining samples in the fragment take to play
double duration = remaining / (double)31400;
// Does the register update occur before the end of the fragment?
if(info.delta <= duration)
{
// If the register update time hasn't already passed then
// process samples upto the point where it should occur
if(info.delta > 0.0)
{
// Process the fragment upto the next TIA register write. We
// round the count passed to process up if needed.
double samples = (31400 * info.delta);
myTIASound.process(stream + ((uInt32)position * channels),
(uInt32)samples + (uInt32)(position + samples) -
((uInt32)position + (uInt32)samples));
position += samples;
remaining -= samples;
}
myTIASound.set(info.addr, info.value);
myRegWriteQueue.dequeue();
}
else
{
// The next register update occurs in the next fragment so finish
// this fragment with the current TIA settings and reduce the register
// update delay by the corresponding amount of time
myTIASound.process(stream + ((uInt32)position * channels),
length - (uInt32)position);
info.delta -= duration;
break;
}
}
}
// double position = 0.0;
// double remaining = length;
//
// while(remaining > 0.0)
// {
// if(myRegWriteQueue.size() == 0)
// {
// // There are no more pending TIA sound register updates so we'll use the current settings to finish filling the sound fragment
// myTIASound.process(stream + ((uInt32)position * 2), length - (uInt32)position);
// myLastRegisterSetCycle = 0;
// break;
// }
// else
// {
// // There are pending TIA sound register updates so we need to
// // update the sound buffer to the point of the next register update
// RegWrite& info = myRegWriteQueue.front();
//
// // How long will the remaining samples in the fragment take to play
// // double duration = remaining / (double)myHardwareSpec.freq;
// double duration = remaining / 31400.0;
//
// // Does the register update occur before the end of the fragment?
// if(info.delta <= duration)
// {
// // If the register update time hasn't already passed then
// // process samples upto the point where it should occur
// if(info.delta > 0.0)
// {
// // Process the fragment upto the next TIA register write. We
// // round the count passed to process up if needed.
// // double samples = (myHardwareSpec.freq * info.delta);
// double samples = (31400.0 * info.delta);
// myTIASound.process(stream + ((uInt32)position * 2), (uInt32)samples + (uInt32)(position + samples) - ((uInt32)position + (uInt32)samples));
//
// position += samples;
// remaining -= samples;
// }
// myTIASound.set(info.addr, info.value);
// myRegWriteQueue.dequeue();
// }
// else
// {
// // The next register update occurs in the next fragment so finish
// // this fragment with the current TIA settings and reduce the register
// // update delay by the corresponding amount of time
// myTIASound.process(stream + ((uInt32)position * 2), length - (uInt32)position);
// info.delta -= duration;
// break;
// }
// }
// }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::callback(void* udata, uInt8* stream, int len)
{
SoundSDL* sound = (SoundSDL*)udata;
if(sound->myIsEnabled)
{
// The callback is requesting 8-bit (unsigned) data, but the TIA sound
// emulator deals in 16-bit (signed) data
// So, we need to convert the pointer and half the length
sound->processFragment((Int16*)stream, (uInt32)len >> 1);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::save(Serializer& out) const
{
try
{
out.putString(name());
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
// Only get the TIA sound registers if sound is enabled
if(myIsInitializedFlag)
{
reg1 = myTIASound.get(0x15);
reg2 = myTIASound.get(0x16);
reg3 = myTIASound.get(0x17);
reg4 = myTIASound.get(0x18);
reg5 = myTIASound.get(0x19);
reg6 = myTIASound.get(0x1a);
}
out.putByte(reg1);
out.putByte(reg2);
out.putByte(reg3);
out.putByte(reg4);
out.putByte(reg5);
out.putByte(reg6);
out.putInt(myLastRegisterSetCycle);
}
catch(...)
{
myOSystem->logMessage("ERROR: SoundSDL::save", 0);
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::load(Serializer& in)
{
try
{
if(in.getString() != name())
return false;
uInt8 reg1 = in.getByte(),
reg2 = in.getByte(),
reg3 = in.getByte(),
reg4 = in.getByte(),
reg5 = in.getByte(),
reg6 = in.getByte();
myLastRegisterSetCycle = (Int32) in.getInt();
// Only update the TIA sound registers if sound is enabled
// Make sure to empty the queue of previous sound fragments
if(myIsInitializedFlag)
{
//SDL_PauseAudio(1);
myRegWriteQueue.clear();
myTIASound.set(0x15, reg1);
myTIASound.set(0x16, reg2);
myTIASound.set(0x17, reg3);
myTIASound.set(0x18, reg4);
myTIASound.set(0x19, reg5);
myTIASound.set(0x1a, reg6);
//if(!myIsMuted) SDL_PauseAudio(0);
}
}
catch(...)
{
myOSystem->logMessage("ERROR: SoundSDL::load", 0);
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::RegWriteQueue::RegWriteQueue(uInt32 capacity)
: myCapacity(capacity),
myBuffer(0),
mySize(0),
myHead(0),
myTail(0)
{
myBuffer = new RegWrite[myCapacity];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::RegWriteQueue::~RegWriteQueue()
{
delete[] myBuffer;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::RegWriteQueue::clear()
{
myHead = myTail = mySize = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::RegWriteQueue::dequeue()
{
if(mySize > 0)
{
myHead = (myHead + 1) % myCapacity;
--mySize;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double SoundSDL::RegWriteQueue::duration()
{
double duration = 0.0;
for(uInt32 i = 0; i < mySize; ++i)
{
duration += myBuffer[(myHead + i) % myCapacity].delta;
}
return duration;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::RegWriteQueue::enqueue(const RegWrite& info)
{
// If an attempt is made to enqueue more than the queue can hold then
// we'll enlarge the queue's capacity.
if(mySize == myCapacity)
grow();
myBuffer[myTail] = info;
myTail = (myTail + 1) % myCapacity;
++mySize;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::RegWrite& SoundSDL::RegWriteQueue::front()
{
assert(mySize != 0);
return myBuffer[myHead];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::RegWriteQueue::size() const
{
return mySize;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::RegWriteQueue::grow()
{
RegWrite* buffer = new RegWrite[myCapacity * 2];
for(uInt32 i = 0; i < mySize; ++i)
{
buffer[i] = myBuffer[(myHead + i) % myCapacity];
}
myHead = 0;
myTail = mySize;
myCapacity = myCapacity * 2;
delete[] myBuffer;
myBuffer = buffer;
}
#endif // SOUND_SUPPORT
+302
View File
@@ -0,0 +1,302 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundSDL.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifdef SOUND_SUPPORT
#ifndef SOUND_SDL_HXX
#define SOUND_SDL_HXX
class OSystem;
//#include <SDL.h>
#include "bspf.hxx"
#include "TIASnd.hxx"
#include "Sound.hxx"
/**
This class implements the sound API for SDL.
@author Stephen Anthony and Bradford W. Mott
@version $Id: SoundSDL.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class SoundSDL : public Sound
{
public:
/**
Create a new sound object. The init method must be invoked before
using the object.
*/
SoundSDL(OSystem* osystem);
/**
Destructor
*/
virtual ~SoundSDL();
public:
/**
Enables/disables the sound subsystem.
@param state True or false, to enable or disable the sound system
*/
void setEnabled(bool state);
/**
The system cycle counter is being adjusting by the specified amount. Any
members using the system cycle counter should be adjusted as needed.
@param amount The amount the cycle counter is being adjusted by
*/
void adjustCycleCounter(Int32 amount);
/**
Sets the number of channels (mono or stereo sound). Note that this
determines how the emulation should 'mix' the channels of the TIA sound
system (of which there are always two). It does not specify the actual
number of hardware channels that SDL should use; it will always attempt
to use two channels in hardware.
@param channels The number of channels
*/
void setChannels(uInt32 channels);
/**
Sets the display framerate. Sound generation for NTSC and PAL games
depends on the framerate, so we need to set it here.
@param framerate The base framerate depending on NTSC or PAL ROM
*/
void setFrameRate(float framerate);
/**
Initializes the sound device. This must be called before any
calls are made to derived methods.
*/
void open();
/**
Should be called to close the sound device. Once called the sound
device can be started again using the open method.
*/
void close();
/**
Set the mute state of the sound object. While muted no sound is played.
@param state Mutes sound if true, unmute if false
*/
void mute(bool state);
/**
Reset the sound device.
*/
void reset();
/**
Sets the sound register to a given value.
@param addr The register address
@param value The value to save into the register
@param cycle The system cycle at which the register is being updated
*/
void set(uInt16 addr, uInt8 value, Int32 cycle);
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. Values outside
this range indicate that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
*/
void setVolume(Int32 percent);
/**
Adjusts the volume of the sound device based on the given direction.
@param direction Increase or decrease the current volume by a predefined
amount based on the direction (1 = increase, -1 = decrease)
*/
void adjustVolume(Int8 direction);
public:
/**
Saves the current state of this device to the given Serializer.
@param out The serializer device to save to.
@return The result of the save. True on success, false on failure.
*/
bool save(Serializer& out) const;
/**
Loads the current state of this device from the given Serializer.
@param in The Serializer device to load from.
@return The result of the load. True on success, false on failure.
*/
bool load(Serializer& in);
/**
Get a descriptor for this console class (used in error checking).
@return The name of the object
*/
string name() const { return "TIASound"; }
public:
/**
Invoked by the sound callback to process the next sound fragment.
The stream is 16-bits (even though the callback is 8-bits), since
the TIASnd class always generates signed 16-bit stereo samples.
@param stream Pointer to the start of the fragment
@param length Length of the fragment
*/
void processFragment(Int16* stream, uInt32 length);
protected:
// Struct to hold information regarding a TIA sound register write
struct RegWrite
{
uInt16 addr;
uInt8 value;
double delta;
};
/**
A queue class used to hold TIA sound register writes before being
processed while creating a sound fragment.
*/
class RegWriteQueue
{
public:
/**
Create a new queue instance with the specified initial
capacity. If the queue ever reaches its capacity then it will
automatically increase its size.
*/
RegWriteQueue(uInt32 capacity = 512);
/**
Destroy this queue instance.
*/
virtual ~RegWriteQueue();
public:
/**
Clear any items stored in the queue.
*/
void clear();
/**
Dequeue the first object in the queue.
*/
void dequeue();
/**
Return the duration of all the items in the queue.
*/
double duration();
/**
Enqueue the specified object.
*/
void enqueue(const RegWrite& info);
/**
Return the item at the front on the queue.
@return The item at the front of the queue.
*/
RegWrite& front();
/**
Answers the number of items currently in the queue.
@return The number of items in the queue.
*/
uInt32 size() const;
private:
// Increase the size of the queue
void grow();
private:
uInt32 myCapacity;
RegWrite* myBuffer;
uInt32 mySize;
uInt32 myHead;
uInt32 myTail;
};
private:
// TIASound emulation object
TIASound myTIASound;
// Indicates if the sound subsystem is to be initialized
bool myIsEnabled;
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag;
// Indicates the cycle when a sound register was last set
Int32 myLastRegisterSetCycle;
// Indicates the number of channels (mono or stereo)
uInt32 myNumChannels;
// Log base 2 of the selected fragment size
double myFragmentSizeLogBase2;
// The myFragmentSizeLogBase2 variable is used in only two places,
// both of which involve an expensive division in the sound
// processing callback
// These are pre-computed to speed up the callback as much as possible
double myFragmentSizeLogDiv1, myFragmentSizeLogDiv2;
// Indicates if the sound is currently muted
bool myIsMuted;
// Current volume as a percentage (0 - 100)
uInt32 myVolume;
// Audio specification structure
typedef struct{
int freq;
uInt16 format;
uInt8 channels;
uInt8 silence;
uInt16 samples;
uInt32 size;
void (*callback)(void *userdata, uInt8 *stream, int len);
void *userdata;
} SDL_AudioSpec;
SDL_AudioSpec myHardwareSpec;
// Queue of TIA register writes
RegWriteQueue myRegWriteQueue;
private:
// Callback function invoked by the SDL Audio library when it needs data
static void callback(void* udata, uInt8* stream, int len);
};
#endif
#endif // SOUND_SUPPORT
+75
View File
@@ -0,0 +1,75 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Stack.hxx 2838 2014-01-17 23:34:03Z stephena $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#ifndef STACK_HXX
#define STACK_HXX
#include <cassert>
/**
* Simple fixed size stack class.
*/
namespace Common {
template <class T, int MAX_SIZE = 10>
class FixedStack
{
public:
FixedStack<T, MAX_SIZE>() : _size(0) {}
bool empty() const { return _size <= 0; }
bool full() const {return _size >= MAX_SIZE; }
void clear() { _size = 0; }
void push(const T& x)
{
assert(_size < MAX_SIZE);
_stack[_size++] = x;
}
T top() const
{
if(_size > 0)
return _stack[_size - 1];
else
return 0;
}
T pop()
{
T tmp;
assert(_size > 0);
tmp = _stack[--_size];
return tmp;
}
int size() const { return _size; }
T operator [](int i) const
{
assert(0 <= i && i < MAX_SIZE);
return _stack[i];
}
protected:
T _stack[MAX_SIZE];
int _size;
};
} // Namespace Common
#endif
+315
View File
@@ -0,0 +1,315 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: StellaKeys.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef STELLA_KEYS_HXX
#define STELLA_KEYS_HXX
#include <SDL.h>
/**
This class implements a thin wrapper around the SDL keysym enumerations,
such that SDL-specific code doesn't have to go into the internal parts of
the codebase. The keycodes are exactly the same, but from the POV of the
rest of the code, they are *KBD* (keyboard) keys, not *SDL* keys.
Once the codebase is ported to SDL2, the intent is to simply change this
file without having to modify all other classes that use StellaKey.
@author Stephen Anthony
*/
// This comes directly from SDL_keysym.h
typedef enum {
/** @name ASCII mapped keysyms
* The keyboard syms have been cleverly chosen to map to ASCII
*/
/*@{*/
KBDK_UNKNOWN = 0,
KBDK_FIRST = 0,
KBDK_BACKSPACE = 8,
KBDK_TAB = 9,
KBDK_CLEAR = 12,
KBDK_RETURN = 13,
KBDK_PAUSE = 19,
KBDK_ESCAPE = 27,
KBDK_SPACE = 32,
KBDK_EXCLAIM = 33,
KBDK_QUOTEDBL = 34,
KBDK_HASH = 35,
KBDK_DOLLAR = 36,
KBDK_AMPERSAND = 38,
KBDK_QUOTE = 39,
KBDK_LEFTPAREN = 40,
KBDK_RIGHTPAREN = 41,
KBDK_ASTERISK = 42,
KBDK_PLUS = 43,
KBDK_COMMA = 44,
KBDK_MINUS = 45,
KBDK_PERIOD = 46,
KBDK_SLASH = 47,
KBDK_0 = 48,
KBDK_1 = 49,
KBDK_2 = 50,
KBDK_3 = 51,
KBDK_4 = 52,
KBDK_5 = 53,
KBDK_6 = 54,
KBDK_7 = 55,
KBDK_8 = 56,
KBDK_9 = 57,
KBDK_COLON = 58,
KBDK_SEMICOLON = 59,
KBDK_LESS = 60,
KBDK_EQUALS = 61,
KBDK_GREATER = 62,
KBDK_QUESTION = 63,
KBDK_AT = 64,
/*
Skip uppercase letters
*/
KBDK_LEFTBRACKET = 91,
KBDK_BACKSLASH = 92,
KBDK_RIGHTBRACKET = 93,
KBDK_CARET = 94,
KBDK_UNDERSCORE = 95,
KBDK_BACKQUOTE = 96,
KBDK_a = 97,
KBDK_b = 98,
KBDK_c = 99,
KBDK_d = 100,
KBDK_e = 101,
KBDK_f = 102,
KBDK_g = 103,
KBDK_h = 104,
KBDK_i = 105,
KBDK_j = 106,
KBDK_k = 107,
KBDK_l = 108,
KBDK_m = 109,
KBDK_n = 110,
KBDK_o = 111,
KBDK_p = 112,
KBDK_q = 113,
KBDK_r = 114,
KBDK_s = 115,
KBDK_t = 116,
KBDK_u = 117,
KBDK_v = 118,
KBDK_w = 119,
KBDK_x = 120,
KBDK_y = 121,
KBDK_z = 122,
KBDK_DELETE = 127,
/* End of ASCII mapped keysyms */
/*@}*/
/** @name International keyboard syms */
/*@{*/
KBDK_WORLD_0 = 160, /* 0xA0 */
KBDK_WORLD_1 = 161,
KBDK_WORLD_2 = 162,
KBDK_WORLD_3 = 163,
KBDK_WORLD_4 = 164,
KBDK_WORLD_5 = 165,
KBDK_WORLD_6 = 166,
KBDK_WORLD_7 = 167,
KBDK_WORLD_8 = 168,
KBDK_WORLD_9 = 169,
KBDK_WORLD_10 = 170,
KBDK_WORLD_11 = 171,
KBDK_WORLD_12 = 172,
KBDK_WORLD_13 = 173,
KBDK_WORLD_14 = 174,
KBDK_WORLD_15 = 175,
KBDK_WORLD_16 = 176,
KBDK_WORLD_17 = 177,
KBDK_WORLD_18 = 178,
KBDK_WORLD_19 = 179,
KBDK_WORLD_20 = 180,
KBDK_WORLD_21 = 181,
KBDK_WORLD_22 = 182,
KBDK_WORLD_23 = 183,
KBDK_WORLD_24 = 184,
KBDK_WORLD_25 = 185,
KBDK_WORLD_26 = 186,
KBDK_WORLD_27 = 187,
KBDK_WORLD_28 = 188,
KBDK_WORLD_29 = 189,
KBDK_WORLD_30 = 190,
KBDK_WORLD_31 = 191,
KBDK_WORLD_32 = 192,
KBDK_WORLD_33 = 193,
KBDK_WORLD_34 = 194,
KBDK_WORLD_35 = 195,
KBDK_WORLD_36 = 196,
KBDK_WORLD_37 = 197,
KBDK_WORLD_38 = 198,
KBDK_WORLD_39 = 199,
KBDK_WORLD_40 = 200,
KBDK_WORLD_41 = 201,
KBDK_WORLD_42 = 202,
KBDK_WORLD_43 = 203,
KBDK_WORLD_44 = 204,
KBDK_WORLD_45 = 205,
KBDK_WORLD_46 = 206,
KBDK_WORLD_47 = 207,
KBDK_WORLD_48 = 208,
KBDK_WORLD_49 = 209,
KBDK_WORLD_50 = 210,
KBDK_WORLD_51 = 211,
KBDK_WORLD_52 = 212,
KBDK_WORLD_53 = 213,
KBDK_WORLD_54 = 214,
KBDK_WORLD_55 = 215,
KBDK_WORLD_56 = 216,
KBDK_WORLD_57 = 217,
KBDK_WORLD_58 = 218,
KBDK_WORLD_59 = 219,
KBDK_WORLD_60 = 220,
KBDK_WORLD_61 = 221,
KBDK_WORLD_62 = 222,
KBDK_WORLD_63 = 223,
KBDK_WORLD_64 = 224,
KBDK_WORLD_65 = 225,
KBDK_WORLD_66 = 226,
KBDK_WORLD_67 = 227,
KBDK_WORLD_68 = 228,
KBDK_WORLD_69 = 229,
KBDK_WORLD_70 = 230,
KBDK_WORLD_71 = 231,
KBDK_WORLD_72 = 232,
KBDK_WORLD_73 = 233,
KBDK_WORLD_74 = 234,
KBDK_WORLD_75 = 235,
KBDK_WORLD_76 = 236,
KBDK_WORLD_77 = 237,
KBDK_WORLD_78 = 238,
KBDK_WORLD_79 = 239,
KBDK_WORLD_80 = 240,
KBDK_WORLD_81 = 241,
KBDK_WORLD_82 = 242,
KBDK_WORLD_83 = 243,
KBDK_WORLD_84 = 244,
KBDK_WORLD_85 = 245,
KBDK_WORLD_86 = 246,
KBDK_WORLD_87 = 247,
KBDK_WORLD_88 = 248,
KBDK_WORLD_89 = 249,
KBDK_WORLD_90 = 250,
KBDK_WORLD_91 = 251,
KBDK_WORLD_92 = 252,
KBDK_WORLD_93 = 253,
KBDK_WORLD_94 = 254,
KBDK_WORLD_95 = 255, /* 0xFF */
/*@}*/
/** @name Numeric keypad */
/*@{*/
KBDK_KP0 = 256,
KBDK_KP1 = 257,
KBDK_KP2 = 258,
KBDK_KP3 = 259,
KBDK_KP4 = 260,
KBDK_KP5 = 261,
KBDK_KP6 = 262,
KBDK_KP7 = 263,
KBDK_KP8 = 264,
KBDK_KP9 = 265,
KBDK_KP_PERIOD = 266,
KBDK_KP_DIVIDE = 267,
KBDK_KP_MULTIPLY = 268,
KBDK_KP_MINUS = 269,
KBDK_KP_PLUS = 270,
KBDK_KP_ENTER = 271,
KBDK_KP_EQUALS = 272,
/*@}*/
/** @name Arrows + Home/End pad */
/*@{*/
KBDK_UP = 273,
KBDK_DOWN = 274,
KBDK_RIGHT = 275,
KBDK_LEFT = 276,
KBDK_INSERT = 277,
KBDK_HOME = 278,
KBDK_END = 279,
KBDK_PAGEUP = 280,
KBDK_PAGEDOWN = 281,
/*@}*/
/** @name Function keys */
/*@{*/
KBDK_F1 = 282,
KBDK_F2 = 283,
KBDK_F3 = 284,
KBDK_F4 = 285,
KBDK_F5 = 286,
KBDK_F6 = 287,
KBDK_F7 = 288,
KBDK_F8 = 289,
KBDK_F9 = 290,
KBDK_F10 = 291,
KBDK_F11 = 292,
KBDK_F12 = 293,
KBDK_F13 = 294,
KBDK_F14 = 295,
KBDK_F15 = 296,
/*@}*/
/** @name Key state modifier keys */
/*@{*/
KBDK_NUMLOCK = 300,
KBDK_CAPSLOCK = 301,
KBDK_SCROLLOCK = 302,
KBDK_RSHIFT = 303,
KBDK_LSHIFT = 304,
KBDK_RCTRL = 305,
KBDK_LCTRL = 306,
KBDK_RALT = 307,
KBDK_LALT = 308,
KBDK_RMETA = 309,
KBDK_LMETA = 310,
KBDK_LSUPER = 311, /**< Left "Windows" key */
KBDK_RSUPER = 312, /**< Right "Windows" key */
KBDK_MODE = 313, /**< "Alt Gr" key */
KBDK_COMPOSE = 314, /**< Multi-key compose key */
/*@}*/
/** @name Miscellaneous function keys */
/*@{*/
KBDK_HELP = 315,
KBDK_PRINT = 316,
KBDK_SYSREQ = 317,
KBDK_BREAK = 318,
KBDK_MENU = 319,
KBDK_POWER = 320, /**< Power Macintosh power key */
KBDK_EURO = 321, /**< Some european keyboards */
KBDK_UNDO = 322, /**< Atari keyboard has Undo */
/*@}*/
/* Add any other keys here */
KBDK_LAST
} StellaKey;
// Just pass SDLMod directly as int (placeholder for now)
// The underlying code doesn't need to know how it's implemented
typedef int StellaMod;
#endif /* StellaKeys */
@@ -8,13 +8,13 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2011 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: StringList.hxx 2234 2011-05-26 16:14:46Z stephena $
// $Id: StringList.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef STRING_LIST_HXX
@@ -23,7 +23,6 @@
#include "Array.hxx"
#include "bspf.hxx"
class StringList : public Common::Array<string>
{
public:
@@ -60,14 +59,4 @@ class StringList : public Common::Array<string>
}
};
class StringMap : public Common::Array< pair<string,string> >
{
public:
void push_back(const string& name, const string& tag)
{
ensureCapacity(_size + 1);
_data[_size++] = make_pair(name, tag);
}
};
#endif
+90
View File
@@ -0,0 +1,90 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: StringParser.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef STRING_PARSER_HXX
#define STRING_PARSER_HXX
#include "StringList.hxx"
#include "bspf.hxx"
/**
This class converts a string into a StringList by splitting on a delimiter
and size.
@author Stephen Anthony
*/
class StringParser
{
public:
/**
Split the given string based on the newline character.
@param str The string to split
*/
StringParser(const string& str)
{
stringstream buf(str);
string line;
while(std::getline(buf, line, '\n'))
myStringList.push_back(line);
}
/**
Split the given string based on the newline character, making sure that
no string is longer than maximum string length.
@param str The string to split
@param maxlen The maximum length of string to generate
*/
StringParser(const string& str, uInt16 maxlen)
{
stringstream buf(str);
string line;
while(std::getline(buf, line, '\n'))
{
size_t beg = 0, len = maxlen, size = line.size();
if(size <= len)
myStringList.push_back(line);
else
{
while((beg+maxlen) < size)
{
size_t spos = line.find_last_of(' ', beg+len);
if(spos > beg)
len = spos - beg;
myStringList.push_back(line.substr(beg, len));
beg += len + 1;
len = maxlen;
}
myStringList.push_back(line.substr(beg));
}
}
}
const StringList& stringList() const { return myStringList; }
private:
StringList myStringList;
};
#endif
+92
View File
@@ -0,0 +1,92 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Variant.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef VARIANT_HXX
#define VARIANT_HXX
#include "Array.hxx"
#include "Rect.hxx"
#include "bspf.hxx"
/**
This class implements a very simple variant type, which is convertible
to several other types. It stores the actual data as a string, and
converts to other types as required. Eventually, this class may be
extended to use templates and become a more full-featured variant type.
@author Stephen Anthony
*/
class Variant
{
private:
// Underlying data store is (currently) always a string
string data;
// Use singleton so we use only one ostringstream object
inline ostringstream& buf() {
static ostringstream buf;
return buf;
}
public:
Variant() : data("") { }
Variant(const string& s) : data(s) { }
Variant(const char* s) : data(s) { }
Variant(int i) { buf().str(""); buf() << i; data = buf().str(); }
Variant(unsigned int i) { buf().str(""); buf() << i; data = buf().str(); }
Variant(float f) { buf().str(""); buf() << f; data = buf().str(); }
Variant(double d) { buf().str(""); buf() << d; data = buf().str(); }
Variant(bool b) { buf().str(""); buf() << b; data = buf().str(); }
Variant(const GUI::Size& s) { buf().str(""); buf() << s; data = buf().str(); }
// Conversion methods
const string& toString() const { return data; }
const char* toCString() const { return data.c_str(); }
const int toInt() const { return atoi(data.c_str()); }
const float toFloat() const { return atof(data.c_str()); }
const bool toBool() const { return data == "1" || data == "true"; }
const GUI::Size toSize() const { return GUI::Size(data); }
// Comparison
bool operator==(const Variant& v) const { return data == v.data; };
bool operator!=(const Variant& v) const { return data != v.data; };
friend ostream& operator<<(ostream& os, const Variant& v) {
os << v.data;
return os;
}
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Variant EmptyVariant("");
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class VariantList : public Common::Array< pair<string,Variant> >
{
public:
void push_back(const Variant& name, const Variant& tag = EmptyVariant)
{
ensureCapacity(_size + 1);
_data[_size++] = make_pair(name.toString(), tag);
}
};
#endif
@@ -8,13 +8,13 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2011 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Version.hxx 2256 2011-06-11 23:55:53Z stephena $
// $Id: Version.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef VERSION_HXX
@@ -22,7 +22,7 @@
#include <cstdlib>
#define STELLA_VERSION "3.4.1"
#define STELLA_BUILD atoi("$Rev: 2256 $" + 6)
#define STELLA_VERSION "3.9.3"
#define STELLA_BUILD atoi("$Rev: 2838 $" + 6)
#endif
+667
View File
@@ -0,0 +1,667 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: ZipHandler.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <cctype>
#include <cstdlib>
#include <zlib.h>
#include "ZipHandler.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ZipHandler::ZipHandler()
: myZip(NULL)
{
for (int cachenum = 0; cachenum < ZIP_CACHE_SIZE; cachenum++)
myZipCache[cachenum] = NULL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ZipHandler::~ZipHandler()
{
zip_file_cache_clear();
free_zip_file(myZip);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ZipHandler::open(const string& filename)
{
// Close already open file
if(myZip)
zip_file_close(myZip);
// And open a new one
zip_file_open(filename.c_str(), &myZip);
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ZipHandler::reset()
{
/* reset the position and go from there */
if(myZip)
myZip->cd_pos = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ZipHandler::hasNext()
{
return myZip && (myZip->cd_pos < myZip->ecd.cd_size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string ZipHandler::next()
{
if(myZip)
{
bool valid = false;
const zip_file_header* header = NULL;
do {
header = zip_file_next_file(myZip);
// Ignore zero-length files and '__MACOSX' virtual directories
valid = header && (header->uncompressed_length > 0) &&
!BSPF_startsWithIgnoreCase(header->filename, "__MACOSX");
}
while(!valid && myZip->cd_pos < myZip->ecd.cd_size);
return valid ? header->filename : EmptyString;
}
else
return EmptyString;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 ZipHandler::decompress(uInt8*& image)
{
static const char* zip_error_s[] = {
"ZIPERR_NONE",
"ZIPERR_OUT_OF_MEMORY",
"ZIPERR_FILE_ERROR",
"ZIPERR_BAD_SIGNATURE",
"ZIPERR_DECOMPRESS_ERROR",
"ZIPERR_FILE_TRUNCATED",
"ZIPERR_FILE_CORRUPT",
"ZIPERR_UNSUPPORTED",
"ZIPERR_BUFFER_TOO_SMALL"
};
if(myZip)
{
uInt32 length = myZip->header.uncompressed_length;
image = new uInt8[length];
ZipHandler::zip_error err = zip_file_decompress(myZip, image, length);
if(err == ZIPERR_NONE)
return length;
else
{
delete[] image; image = 0;
length = 0;
throw zip_error_s[err];
}
}
else
throw "Invalid ZIP archive";
}
/*-------------------------------------------------
replaces functionality of various osd_xxx
file access functions
-------------------------------------------------*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ZipHandler::stream_open(const char* filename, fstream** stream,
uInt64& length)
{
fstream* in = new fstream(filename, fstream::in | fstream::binary);
if(!in || !in->is_open())
{
*stream = NULL;
length = 0;
return false;
}
else
{
in->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
*stream = in;
in->seekg(0, ios::end);
length = in->tellg();
in->seekg(0, ios::beg);
return true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ZipHandler::stream_close(fstream** stream)
{
if(*stream)
{
if((*stream)->is_open())
(*stream)->close();
delete *stream;
*stream = NULL;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ZipHandler::stream_read(fstream* stream, void* buffer, uInt64 offset,
uInt32 length, uInt32& actual)
{
try
{
stream->seekg(offset);
stream->read((char*)buffer, length);
actual = stream->gcount();
return true;
}
catch(...)
{
return false;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*-------------------------------------------------
zip_file_open - opens a ZIP file for reading
-------------------------------------------------*/
ZipHandler::zip_error ZipHandler::zip_file_open(const char *filename, zip_file **zip)
{
zip_error ziperr = ZIPERR_NONE;
uInt32 read_length;
zip_file *newzip;
char *string;
int cachenum;
bool success;
/* ensure we start with a NULL result */
*zip = NULL;
/* see if we are in the cache, and reopen if so */
for (cachenum = 0; cachenum < ZIP_CACHE_SIZE; cachenum++)
{
zip_file *cached = myZipCache[cachenum];
/* if we have a valid entry and it matches our filename, use it and remove
from the cache */
if (cached != NULL && cached->filename != NULL &&
strcmp(filename, cached->filename) == 0)
{
*zip = cached;
myZipCache[cachenum] = NULL;
return ZIPERR_NONE;
}
}
/* allocate memory for the zip_file structure */
newzip = (zip_file *)malloc(sizeof(*newzip));
if (newzip == NULL)
return ZIPERR_OUT_OF_MEMORY;
memset(newzip, 0, sizeof(*newzip));
/* open the file */
if(!stream_open(filename, &newzip->file, newzip->length))
{
ziperr = ZIPERR_FILE_ERROR;
goto error;
}
/* read ecd data */
ziperr = read_ecd(newzip);
if (ziperr != ZIPERR_NONE)
goto error;
/* verify that we can work with this zipfile (no disk spanning allowed) */
if (newzip->ecd.disk_number != newzip->ecd.cd_start_disk_number ||
newzip->ecd.cd_disk_entries != newzip->ecd.cd_total_entries)
{
ziperr = ZIPERR_UNSUPPORTED;
goto error;
}
/* allocate memory for the central directory */
newzip->cd = (uInt8 *)malloc(newzip->ecd.cd_size + 1);
if (newzip->cd == NULL)
{
ziperr = ZIPERR_OUT_OF_MEMORY;
goto error;
}
/* read the central directory */
success = stream_read(newzip->file, newzip->cd, newzip->ecd.cd_start_disk_offset,
newzip->ecd.cd_size, read_length);
if (!success || read_length != newzip->ecd.cd_size)
{
ziperr = success ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR;
goto error;
}
/* make a copy of the filename for caching purposes */
string = (char *)malloc(strlen(filename) + 1);
if (string == NULL)
{
ziperr = ZIPERR_OUT_OF_MEMORY;
goto error;
}
strcpy(string, filename);
newzip->filename = string;
*zip = newzip;
// Count ROM files (we do it at this level so it will be cached)
while(hasNext())
{
const std::string& file = next();
if(BSPF_endsWithIgnoreCase(file, ".a26") ||
BSPF_endsWithIgnoreCase(file, ".bin") ||
BSPF_endsWithIgnoreCase(file, ".rom"))
(*zip)->romfiles++;
}
return ZIPERR_NONE;
error:
free_zip_file(newzip);
return ziperr;
}
/*-------------------------------------------------
zip_file_close - close a ZIP file and add it
to the cache
-------------------------------------------------*/
void ZipHandler::zip_file_close(zip_file *zip)
{
int cachenum;
/* close the open files */
if (zip->file)
stream_close(&zip->file);
/* find the first NULL entry in the cache */
for (cachenum = 0; cachenum < ZIP_CACHE_SIZE; cachenum++)
if (myZipCache[cachenum] == NULL)
break;
/* if no room left in the cache, free the bottommost entry */
if (cachenum == ZIP_CACHE_SIZE)
free_zip_file(myZipCache[--cachenum]);
/* move everyone else down and place us at the top */
if (cachenum != 0)
memmove(&myZipCache[1], &myZipCache[0], cachenum * sizeof(myZipCache[0]));
myZipCache[0] = zip;
}
/*-------------------------------------------------
zip_file_cache_clear - clear the ZIP file
cache and free all memory
-------------------------------------------------*/
void ZipHandler::zip_file_cache_clear(void)
{
/* clear call cache entries */
for (int cachenum = 0; cachenum < ZIP_CACHE_SIZE; cachenum++)
if (myZipCache[cachenum] != NULL)
{
free_zip_file(myZipCache[cachenum]);
myZipCache[cachenum] = NULL;
}
}
/***************************************************************************
CONTAINED FILE ACCESS
***************************************************************************/
/*-------------------------------------------------
zip_file_next_entry - return the next entry
in the ZIP
-------------------------------------------------*/
const ZipHandler::zip_file_header* ZipHandler::zip_file_next_file(zip_file *zip)
{
/* fix up any modified data */
if (zip->header.raw != NULL)
{
zip->header.raw[ZIPCFN + zip->header.filename_length] = zip->header.saved;
zip->header.raw = NULL;
}
/* if we're at or past the end, we're done */
if (zip->cd_pos >= zip->ecd.cd_size)
return NULL;
/* extract file header info */
zip->header.raw = zip->cd + zip->cd_pos;
zip->header.rawlength = ZIPCFN;
zip->header.signature = read_dword(zip->header.raw + ZIPCENSIG);
zip->header.version_created = read_word (zip->header.raw + ZIPCVER);
zip->header.version_needed = read_word (zip->header.raw + ZIPCVXT);
zip->header.bit_flag = read_word (zip->header.raw + ZIPCFLG);
zip->header.compression = read_word (zip->header.raw + ZIPCMTHD);
zip->header.file_time = read_word (zip->header.raw + ZIPCTIM);
zip->header.file_date = read_word (zip->header.raw + ZIPCDAT);
zip->header.crc = read_dword(zip->header.raw + ZIPCCRC);
zip->header.compressed_length = read_dword(zip->header.raw + ZIPCSIZ);
zip->header.uncompressed_length = read_dword(zip->header.raw + ZIPCUNC);
zip->header.filename_length = read_word (zip->header.raw + ZIPCFNL);
zip->header.extra_field_length = read_word (zip->header.raw + ZIPCXTL);
zip->header.file_comment_length = read_word (zip->header.raw + ZIPCCML);
zip->header.start_disk_number = read_word (zip->header.raw + ZIPDSK);
zip->header.internal_attributes = read_word (zip->header.raw + ZIPINT);
zip->header.external_attributes = read_dword(zip->header.raw + ZIPEXT);
zip->header.local_header_offset = read_dword(zip->header.raw + ZIPOFST);
zip->header.filename = (char *)zip->header.raw + ZIPCFN;
/* make sure we have enough data */
zip->header.rawlength += zip->header.filename_length;
zip->header.rawlength += zip->header.extra_field_length;
zip->header.rawlength += zip->header.file_comment_length;
if (zip->cd_pos + zip->header.rawlength > zip->ecd.cd_size)
return NULL;
/* NULL terminate the filename */
zip->header.saved = zip->header.raw[ZIPCFN + zip->header.filename_length];
zip->header.raw[ZIPCFN + zip->header.filename_length] = 0;
/* advance the position */
zip->cd_pos += zip->header.rawlength;
return &zip->header;
}
/*-------------------------------------------------
zip_file_decompress - decompress a file
from a ZIP into the target buffer
-------------------------------------------------*/
ZipHandler::zip_error
ZipHandler::zip_file_decompress(zip_file *zip, void *buffer, uInt32 length)
{
zip_error ziperr;
uInt64 offset;
/* if we don't have enough buffer, error */
if (length < zip->header.uncompressed_length)
return ZIPERR_BUFFER_TOO_SMALL;
/* make sure the info in the header aligns with what we know */
if (zip->header.start_disk_number != zip->ecd.disk_number)
return ZIPERR_UNSUPPORTED;
/* get the compressed data offset */
ziperr = get_compressed_data_offset(zip, &offset);
if (ziperr != ZIPERR_NONE)
return ziperr;
/* handle compression types */
switch (zip->header.compression)
{
case 0:
ziperr = decompress_data_type_0(zip, offset, buffer, length);
break;
case 8:
ziperr = decompress_data_type_8(zip, offset, buffer, length);
break;
default:
ziperr = ZIPERR_UNSUPPORTED;
break;
}
return ziperr;
}
/***************************************************************************
CACHE MANAGEMENT
***************************************************************************/
/*-------------------------------------------------
free_zip_file - free all the data for a
zip_file
-------------------------------------------------*/
void ZipHandler::free_zip_file(zip_file *zip)
{
if (zip != NULL)
{
if (zip->file)
stream_close(&zip->file);
if (zip->filename != NULL)
free((void *)zip->filename);
if (zip->ecd.raw != NULL)
free(zip->ecd.raw);
if (zip->cd != NULL)
free(zip->cd);
free(zip);
}
}
/***************************************************************************
ZIP FILE PARSING
***************************************************************************/
/*-------------------------------------------------
read_ecd - read the ECD data
-------------------------------------------------*/
ZipHandler::zip_error ZipHandler::read_ecd(zip_file *zip)
{
uInt32 buflen = 1024;
uInt8 *buffer;
/* we may need multiple tries */
while (buflen < 65536)
{
uInt32 read_length;
Int32 offset;
/* max out the buffer length at the size of the file */
if (buflen > zip->length)
buflen = zip->length;
/* allocate buffer */
buffer = (uInt8 *)malloc(buflen + 1);
if (buffer == NULL)
return ZIPERR_OUT_OF_MEMORY;
/* read in one buffers' worth of data */
bool success = stream_read(zip->file, buffer, zip->length - buflen,
buflen, read_length);
if (!success || read_length != buflen)
{
free(buffer);
return ZIPERR_FILE_ERROR;
}
/* find the ECD signature */
for (offset = buflen - 22; offset >= 0; offset--)
if (buffer[offset + 0] == 'P' && buffer[offset + 1] == 'K' &&
buffer[offset + 2] == 0x05 && buffer[offset + 3] == 0x06)
break;
/* if we found it, fill out the data */
if (offset >= 0)
{
/* reuse the buffer as our ECD buffer */
zip->ecd.raw = buffer;
zip->ecd.rawlength = buflen - offset;
/* append a NULL terminator to the comment */
memmove(&buffer[0], &buffer[offset], zip->ecd.rawlength);
zip->ecd.raw[zip->ecd.rawlength] = 0;
/* extract ecd info */
zip->ecd.signature = read_dword(zip->ecd.raw + ZIPESIG);
zip->ecd.disk_number = read_word (zip->ecd.raw + ZIPEDSK);
zip->ecd.cd_start_disk_number = read_word (zip->ecd.raw + ZIPECEN);
zip->ecd.cd_disk_entries = read_word (zip->ecd.raw + ZIPENUM);
zip->ecd.cd_total_entries = read_word (zip->ecd.raw + ZIPECENN);
zip->ecd.cd_size = read_dword(zip->ecd.raw + ZIPECSZ);
zip->ecd.cd_start_disk_offset = read_dword(zip->ecd.raw + ZIPEOFST);
zip->ecd.comment_length = read_word (zip->ecd.raw + ZIPECOML);
zip->ecd.comment = (const char *)(zip->ecd.raw + ZIPECOM);
return ZIPERR_NONE;
}
/* didn't find it; free this buffer and expand our search */
free(buffer);
if (buflen < zip->length)
buflen *= 2;
else
return ZIPERR_BAD_SIGNATURE;
}
return ZIPERR_OUT_OF_MEMORY;
}
/*-------------------------------------------------
get_compressed_data_offset - return the
offset of the compressed data
-------------------------------------------------*/
ZipHandler::zip_error
ZipHandler::get_compressed_data_offset(zip_file *zip, uInt64 *offset)
{
uInt32 read_length;
/* make sure the file handle is open */
if (zip->file == NULL && !stream_open(zip->filename, &zip->file, zip->length))
return ZIPERR_FILE_ERROR;
/* now go read the fixed-sized part of the local file header */
bool success = stream_read(zip->file, zip->buffer, zip->header.local_header_offset,
ZIPNAME, read_length);
if (!success || read_length != ZIPNAME)
return success ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR;
/* compute the final offset */
*offset = zip->header.local_header_offset + ZIPNAME;
*offset += read_word(zip->buffer + ZIPFNLN);
*offset += read_word(zip->buffer + ZIPXTRALN);
return ZIPERR_NONE;
}
/***************************************************************************
DECOMPRESSION INTERFACES
***************************************************************************/
/*-------------------------------------------------
decompress_data_type_0 - "decompress"
type 0 data (which is uncompressed)
-------------------------------------------------*/
ZipHandler::zip_error
ZipHandler::decompress_data_type_0(zip_file *zip, uInt64 offset,
void *buffer, uInt32 length)
{
uInt32 read_length;
/* the data is uncompressed; just read it */
bool success = stream_read(zip->file, buffer, offset, zip->header.compressed_length,
read_length);
if (!success)
return ZIPERR_FILE_ERROR;
else if (read_length != zip->header.compressed_length)
return ZIPERR_FILE_TRUNCATED;
else
return ZIPERR_NONE;
}
/*-------------------------------------------------
decompress_data_type_8 - decompress
type 8 data (which is deflated)
-------------------------------------------------*/
ZipHandler::zip_error
ZipHandler::decompress_data_type_8(zip_file *zip, uInt64 offset,
void *buffer, uInt32 length)
{
uInt32 input_remaining = zip->header.compressed_length;
uInt32 read_length;
z_stream stream;
int zerr;
#if 0
// TODO - check newer versions of ZIP, and determine why this specific
// version (0x14) is important
/* make sure we don't need a newer mechanism */
if (zip->header.version_needed > 0x14)
return ZIPERR_UNSUPPORTED;
#endif
/* reset the stream */
memset(&stream, 0, sizeof(stream));
stream.next_out = (Bytef *)buffer;
stream.avail_out = length;
/* initialize the decompressor */
zerr = inflateInit2(&stream, -MAX_WBITS);
if (zerr != Z_OK)
return ZIPERR_DECOMPRESS_ERROR;
/* loop until we're done */
while (1)
{
/* read in the next chunk of data */
bool success = stream_read(zip->file, zip->buffer, offset,
BSPF_min(input_remaining, (uInt32)sizeof(zip->buffer)),
read_length);
if (!success)
{
inflateEnd(&stream);
return ZIPERR_FILE_ERROR;
}
offset += read_length;
/* if we read nothing, but still have data left, the file is truncated */
if (read_length == 0 && input_remaining > 0)
{
inflateEnd(&stream);
return ZIPERR_FILE_TRUNCATED;
}
/* fill out the input data */
stream.next_in = zip->buffer;
stream.avail_in = read_length;
input_remaining -= read_length;
/* add a dummy byte at end of compressed data */
if (input_remaining == 0)
stream.avail_in++;
/* now inflate */
zerr = inflate(&stream, Z_NO_FLUSH);
if (zerr == Z_STREAM_END)
break;
if (zerr != Z_OK)
{
inflateEnd(&stream);
return ZIPERR_DECOMPRESS_ERROR;
}
}
/* finish decompression */
zerr = inflateEnd(&stream);
if (zerr != Z_OK)
return ZIPERR_DECOMPRESS_ERROR;
/* if anything looks funny, report an error */
if (stream.avail_out > 0 || input_remaining > 0)
return ZIPERR_DECOMPRESS_ERROR;
return ZIPERR_NONE;
}
+273
View File
@@ -0,0 +1,273 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: ZipHandler.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef ZIP_HANDLER_HXX
#define ZIP_HANDLER_HXX
#include <fstream>
#include "bspf.hxx"
/***************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#define ZIP_DECOMPRESS_BUFSIZE 16384
/**
This class implements a thin wrapper around the zip file management code
from the MAME project.
@author Wrapper class by Stephen Anthony, with main functionality
by Aaron Giles
*/
class ZipHandler
{
public:
ZipHandler();
virtual ~ZipHandler();
// Open ZIP file for processing
void open(const string& filename);
// The following form an iterator for processing the filenames in the ZIP file
void reset(); // Reset iterator to first file
bool hasNext(); // Answer whether there are more files present
string next(); // Get next file
// Decompress the currently selected file and return its length
// An exception will be thrown on any errors
uInt32 decompress(uInt8*& image);
// Answer the number of ROM files found in the archive
// Currently, this means files with extension a26/bin/rom
uInt16 romFiles() const { return myZip ? myZip->romfiles : 0; }
private:
// Replaces functionaity of various osd_xxxx functions
static bool stream_open(const char* filename, fstream** stream, uInt64& length);
static void stream_close(fstream** stream);
static bool stream_read(fstream* stream, void* buffer, uInt64 offset,
uInt32 length, uInt32& actual);
/* Error types */
enum zip_error
{
ZIPERR_NONE = 0,
ZIPERR_OUT_OF_MEMORY,
ZIPERR_FILE_ERROR,
ZIPERR_BAD_SIGNATURE,
ZIPERR_DECOMPRESS_ERROR,
ZIPERR_FILE_TRUNCATED,
ZIPERR_FILE_CORRUPT,
ZIPERR_UNSUPPORTED,
ZIPERR_BUFFER_TOO_SMALL
};
/* contains extracted file header information */
struct zip_file_header
{
uInt32 signature; /* central file header signature */
uInt16 version_created; /* version made by */
uInt16 version_needed; /* version needed to extract */
uInt16 bit_flag; /* general purpose bit flag */
uInt16 compression; /* compression method */
uInt16 file_time; /* last mod file time */
uInt16 file_date; /* last mod file date */
uInt32 crc; /* crc-32 */
uInt32 compressed_length; /* compressed size */
uInt32 uncompressed_length; /* uncompressed size */
uInt16 filename_length; /* filename length */
uInt16 extra_field_length; /* extra field length */
uInt16 file_comment_length; /* file comment length */
uInt16 start_disk_number; /* disk number start */
uInt16 internal_attributes; /* internal file attributes */
uInt32 external_attributes; /* external file attributes */
uInt32 local_header_offset; /* relative offset of local header */
const char* filename; /* filename */
uInt8* raw; /* pointer to the raw data */
uInt32 rawlength; /* length of the raw data */
uInt8 saved; /* saved byte from after filename */
};
/* contains extracted end of central directory information */
struct zip_ecd
{
uInt32 signature; /* end of central dir signature */
uInt16 disk_number; /* number of this disk */
uInt16 cd_start_disk_number; /* number of the disk with the start of the central directory */
uInt16 cd_disk_entries; /* total number of entries in the central directory on this disk */
uInt16 cd_total_entries; /* total number of entries in the central directory */
uInt32 cd_size; /* size of the central directory */
uInt32 cd_start_disk_offset; /* offset of start of central directory with respect to the starting disk number */
uInt16 comment_length; /* .ZIP file comment length */
const char* comment; /* .ZIP file comment */
uInt8* raw; /* pointer to the raw data */
uInt32 rawlength; /* length of the raw data */
};
/* describes an open ZIP file */
struct zip_file
{
const char* filename; /* copy of ZIP filename (for caching) */
fstream* file; /* C++ fstream file handle */
uInt64 length; /* length of zip file */
uInt16 romfiles; /* number of ROM files in central directory */
zip_ecd ecd; /* end of central directory */
uInt8* cd; /* central directory raw data */
uInt32 cd_pos; /* position in central directory */
zip_file_header header; /* current file header */
uInt8 buffer[ZIP_DECOMPRESS_BUFSIZE]; /* buffer for decompression */
};
enum {
/* number of open files to cache */
ZIP_CACHE_SIZE = 8,
/* offsets in end of central directory structure */
ZIPESIG = 0x00,
ZIPEDSK = 0x04,
ZIPECEN = 0x06,
ZIPENUM = 0x08,
ZIPECENN = 0x0a,
ZIPECSZ = 0x0c,
ZIPEOFST = 0x10,
ZIPECOML = 0x14,
ZIPECOM = 0x16,
/* offsets in central directory entry structure */
ZIPCENSIG = 0x00,
ZIPCVER = 0x04,
ZIPCOS = 0x05,
ZIPCVXT = 0x06,
ZIPCEXOS = 0x07,
ZIPCFLG = 0x08,
ZIPCMTHD = 0x0a,
ZIPCTIM = 0x0c,
ZIPCDAT = 0x0e,
ZIPCCRC = 0x10,
ZIPCSIZ = 0x14,
ZIPCUNC = 0x18,
ZIPCFNL = 0x1c,
ZIPCXTL = 0x1e,
ZIPCCML = 0x20,
ZIPDSK = 0x22,
ZIPINT = 0x24,
ZIPEXT = 0x26,
ZIPOFST = 0x2a,
ZIPCFN = 0x2e,
/* offsets in local file header structure */
ZIPLOCSIG = 0x00,
ZIPVER = 0x04,
ZIPGENFLG = 0x06,
ZIPMTHD = 0x08,
ZIPTIME = 0x0a,
ZIPDATE = 0x0c,
ZIPCRC = 0x0e,
ZIPSIZE = 0x12,
ZIPUNCMP = 0x16,
ZIPFNLN = 0x1a,
ZIPXTRALN = 0x1c,
ZIPNAME = 0x1e
};
private:
/* ----- ZIP file access ----- */
/* open a ZIP file and parse its central directory */
zip_error zip_file_open(const char *filename, zip_file **zip);
/* close a ZIP file (may actually be left open due to caching) */
void zip_file_close(zip_file *zip);
/* clear out all open ZIP files from the cache */
void zip_file_cache_clear(void);
/* ----- contained file access ----- */
/* find the next file in the ZIP */
const zip_file_header *zip_file_next_file(zip_file *zip);
/* decompress the most recently found file in the ZIP */
zip_error zip_file_decompress(zip_file *zip, void *buffer, uInt32 length);
inline static uInt16 read_word(uInt8* buf)
{
return (buf[1] << 8) | buf[0];
}
inline static uInt32 read_dword(uInt8* buf)
{
return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
}
/* cache management */
static void free_zip_file(zip_file *zip);
/* ZIP file parsing */
static zip_error read_ecd(zip_file *zip);
static zip_error get_compressed_data_offset(zip_file *zip, uInt64 *offset);
/* decompression interfaces */
static zip_error decompress_data_type_0(zip_file *zip, uInt64 offset,
void *buffer, uInt32 length);
static zip_error decompress_data_type_8(zip_file *zip, uInt64 offset,
void *buffer, uInt32 length);
private:
zip_file* myZip;
zip_file* myZipCache[ZIP_CACHE_SIZE];
};
#endif /* ZIP_HANDLER_HXX */
@@ -8,13 +8,13 @@
// BB BB SS SS PP FF
// BBBBB SSSS PP FF
//
// Copyright (c) 1995-2011 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: bspf.hxx 2232 2011-05-24 16:04:48Z stephena $
// $Id: bspf.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef BSPF_HXX
@@ -25,7 +25,7 @@
that need to be defined for different operating systems.
@author Bradford W. Mott
@version $Id: bspf.hxx 2232 2011-05-24 16:04:48Z stephena $
@version $Id: bspf.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
#ifdef HAVE_INTTYPES
@@ -57,7 +57,7 @@
typedef __int64 Int64;
typedef unsigned __int64 uInt64;
#else
#error Update BSPF.hxx for datatypes
#error Update src/common/bspf.hxx for datatypes
#endif
@@ -70,35 +70,16 @@
#include <sstream>
#include <cstring>
#include <cctype>
#include <cstdio>
using namespace std;
// Defines to help with path handling
#if defined BSPF_UNIX
#if (defined(BSPF_UNIX) || defined(BSPF_MAC_OSX))
#define BSPF_PATH_SEPARATOR "/"
#elif (defined(BSPF_DOS) || defined(BSPF_WIN32) || defined(BSPF_OS2))
#define BSPF_PATH_SEPARATOR "\\"
#elif defined BSPF_MAC_OSX
#define BSPF_PATH_SEPARATOR "/"
#elif defined BSPF_GP2X
#define BSPF_PATH_SEPARATOR "/"
#endif
// I wish Windows had a complete POSIX layer
#if defined BSPF_WIN32 && !defined __GNUG__
#define BSPF_strcasecmp stricmp
#define BSPF_strncasecmp strnicmp
#define BSPF_isblank(c) ((c == ' ') || (c == '\t'))
#define BSPF_snprintf _snprintf
#define BSPF_vsnprintf _vsnprintf
#else
//ROBO:
// #include <strings.h>
#include <string.h>
#define BSPF_strcasecmp strcasecmp
#define BSPF_strncasecmp strncasecmp
#define BSPF_isblank(c) isblank(c)
#define BSPF_snprintf snprintf
#define BSPF_vsnprintf vsnprintf
#error Update src/common/bspf.hxx for path separator
#endif
// CPU architecture type
@@ -113,50 +94,100 @@ using namespace std;
#define BSPF_ARCH "NOARCH"
#endif
// Used for stringstreams
#define HEX8 uppercase << hex << setw(8) << setfill('0')
#define HEX4 uppercase << hex << setw(4) << setfill('0')
#define HEX2 uppercase << hex << setw(2) << setfill('0')
// I wish Windows had a complete POSIX layer
#if defined BSPF_WIN32 && !defined __GNUG__
#define BSPF_snprintf _snprintf
#define BSPF_vsnprintf _vsnprintf
#else
#define HAVE_UNISTD_H // needed for building zlib
#include <strings.h>
#define BSPF_snprintf snprintf
#define BSPF_vsnprintf vsnprintf
#endif
static const string EmptyString("");
//////////////////////////////////////////////////////////////////////
// Some convenience functions
template<typename T> inline void BSPF_swap(T& a, T& b) { T tmp = a; a = b; b = tmp; }
template<typename T> inline T BSPF_abs (T x) { return (x>=0) ? x : -x; }
template<typename T> inline T BSPF_min (T a, T b) { return (a<b) ? a : b; }
template<typename T> inline T BSPF_max (T a, T b) { return (a>b) ? a : b; }
// Test whether two strings are equal (case insensitive)
inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2)
{
return BSPF_strcasecmp(s1.c_str(), s2.c_str()) == 0;
}
inline bool BSPF_equalsIgnoreCase(const char* s1, const char* s2)
{
return BSPF_strcasecmp(s1, s2) == 0;
}
// Test whether the first string starts with the second one (case insensitive)
inline bool BSPF_startsWithIgnoreCase(const string& s1, const string& s2)
{
return BSPF_strncasecmp(s1.c_str(), s2.c_str(), s2.length()) == 0;
}
inline bool BSPF_startsWithIgnoreCase(const char* s1, const char* s2)
{
return BSPF_strncasecmp(s1, s2, strlen(s2)) == 0;
}
template<typename T> inline T BSPF_clamp (T a, T l, T u) { return (a<l) ? l : (a>u) ? u : a; }
// Test whether two characters are equal (case insensitive)
static bool BSPF_equalsIgnoreCaseChar(char ch1, char ch2)
{
return toupper((unsigned char)ch1) == toupper((unsigned char)ch2);
}
// Find location (if any) of the second string within the first
inline size_t BSPF_findIgnoreCase(const string& s1, const string& s2)
// Compare two strings, ignoring case
inline int BSPF_compareIgnoreCase(const string& s1, const string& s2)
{
string::const_iterator pos = std::search(s1.begin(), s1.end(),
s2.begin(), s2.end(), BSPF_equalsIgnoreCaseChar);
return pos == s1.end() ? string::npos : pos - s1.begin();
#if defined WIN32 && !defined __GNUG__
return _stricmp(s1.c_str(), s2.c_str());
#else
return strcasecmp(s1.c_str(), s2.c_str());
#endif
}
inline int BSPF_compareIgnoreCase(const char* s1, const char* s2)
{
#if defined WIN32 && !defined __GNUG__
return _stricmp(s1, s2);
#else
return strcasecmp(s1, s2);
#endif
}
static const string EmptyString("");
// Test whether the first string starts with the second one (case insensitive)
inline bool BSPF_startsWithIgnoreCase(const string& s1, const string& s2)
{
#if defined WIN32 && !defined __GNUG__
return _strnicmp(s1.c_str(), s2.c_str(), s2.length()) == 0;
#else
return strncasecmp(s1.c_str(), s2.c_str(), s2.length()) == 0;
#endif
}
inline bool BSPF_startsWithIgnoreCase(const char* s1, const char* s2)
{
#if defined WIN32 && !defined __GNUG__
return _strnicmp(s1, s2, strlen(s2)) == 0;
#else
return strncasecmp(s1, s2, strlen(s2)) == 0;
#endif
}
// Test whether two strings are equal (case insensitive)
inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2)
{
return BSPF_compareIgnoreCase(s1, s2) == 0;
}
// Find location (if any) of the second string within the first,
// starting from 'startpos' in the first string
inline size_t BSPF_findIgnoreCase(const string& s1, const string& s2, int startpos = 0)
{
string::const_iterator pos = std::search(s1.begin()+startpos, s1.end(),
s2.begin(), s2.end(), BSPF_equalsIgnoreCaseChar);
return pos == s1.end() ? string::npos : pos - (s1.begin()+startpos);
}
// Test whether the first string ends with the second one (case insensitive)
inline bool BSPF_endsWithIgnoreCase(const string& s1, const string& s2)
{
if(s1.length() >= s2.length())
{
const char* end = s1.c_str() + s1.length() - s2.length();
return BSPF_compareIgnoreCase(end, s2.c_str()) == 0;
}
return false;
}
// Test whether the first string contains the second one (case insensitive)
inline bool BSPF_containsIgnoreCase(const string& s1, const string& s2)
{
return BSPF_findIgnoreCase(s1, s2) != string::npos;
}
#endif
+215
View File
@@ -0,0 +1,215 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: mainSDL.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <SDL.h>
#include <cstdlib>
#include "bspf.hxx"
#include "Console.hxx"
#include "Event.hxx"
#include "EventHandler.hxx"
#include "FrameBuffer.hxx"
#include "PropsSet.hxx"
#include "Sound.hxx"
#include "Settings.hxx"
#include "FSNode.hxx"
#include "OSystem.hxx"
#include "System.hxx"
#if defined(UNIX)
#include "SettingsUNIX.hxx"
#include "OSystemUNIX.hxx"
#elif defined(WIN32)
#include "SettingsWin32.hxx"
#include "OSystemWin32.hxx"
#elif defined(MAC_OSX)
#include "SettingsMACOSX.hxx"
#include "OSystemMACOSX.hxx"
extern "C" {
int stellaMain(int argc, char* argv[]);
}
#else
#error Unsupported platform!
#endif
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
#endif
#ifdef CHEATCODE_SUPPORT
#include "CheatManager.hxx"
#endif
// Pointer to the main parent osystem object or the null pointer
OSystem* theOSystem = (OSystem*) NULL;
// Does general Cleanup in case any operation failed (or at end of program)
int Cleanup()
{
theOSystem->logMessage("Cleanup from mainSDL", 2);
theOSystem->saveConfig();
if(theOSystem)
delete theOSystem;
if(SDL_WasInit(SDL_INIT_VIDEO) & SDL_INIT_VIDEO)
SDL_Quit();
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if defined(MAC_OSX)
int stellaMain(int argc, char* argv[])
#else
int main(int argc, char* argv[])
#endif
{
ios_base::sync_with_stdio(false);
// Create the parent OSystem object and settings
#if defined(UNIX)
theOSystem = new OSystemUNIX();
SettingsUNIX settings(theOSystem);
#elif defined(WIN32)
theOSystem = new OSystemWin32();
SettingsWin32 settings(theOSystem);
#elif defined(MAC_OSX)
theOSystem = new OSystemMACOSX();
SettingsMACOSX settings(theOSystem);
#else
#error Unsupported platform!
#endif
theOSystem->loadConfig();
theOSystem->logMessage("Loading config options ...", 2);
// Take care of commandline arguments
theOSystem->logMessage("Loading commandline arguments ...", 2);
string romfile = theOSystem->settings().loadCommandLine(argc, argv);
// Finally, make sure the settings are valid
// We do it once here, so the rest of the program can assume valid settings
theOSystem->logMessage("Validating config options ...", 2);
theOSystem->settings().validate();
// Create the full OSystem after the settings, since settings are
// probably needed for defaults
theOSystem->logMessage("Creating the OSystem ...", 2);
if(!theOSystem->create())
{
theOSystem->logMessage("ERROR: Couldn't create OSystem", 0);
return Cleanup();
}
// Check to see if the user requested info about a specific ROM,
// or the list of internal ROMs
// If so, show the information and immediately exit
if(theOSystem->settings().getBool("listrominfo"))
{
theOSystem->logMessage("Showing output from 'listrominfo' ...", 2);
theOSystem->propSet().print();
return Cleanup();
}
else if(theOSystem->settings().getBool("rominfo"))
{
theOSystem->logMessage("Showing output from 'rominfo' ...", 2);
FilesystemNode romnode(romfile);
theOSystem->logMessage(theOSystem->getROMInfo(romnode), 0);
return Cleanup();
}
else if(theOSystem->settings().getBool("help"))
{
theOSystem->logMessage("Displaying usage", 2);
theOSystem->settings().usage();
return Cleanup();
}
#ifdef BSPF_UNIX
// Nvidia cards under UNIX don't currently support SDL_GL_SWAP_CONTROL
// So we need to do it with an Nvidia-specific environment variable
// This also means the setting can only be changed by restarting Stella
// This functionality should really be integrated into SDL directly
if(theOSystem->settings().getBool("gl_vsync"))
putenv((char*)"__GL_SYNC_TO_VBLANK=1");
#endif
//// Main loop ////
// First we check if a ROM is specified on the commandline. If so, and if
// the ROM actually exists, use it to create a new console.
// Next we check if a directory is specified on the commandline. If so,
// open the rom launcher in that directory.
// If not, use the built-in ROM launcher. In this case, we enter 'launcher'
// mode and let the main event loop take care of opening a new console/ROM.
FilesystemNode romnode(romfile);
if(romfile == "" || romnode.isDirectory())
{
theOSystem->logMessage("Attempting to use ROM launcher ...", 2);
bool launcherOpened = romfile != "" ?
theOSystem->createLauncher(romnode.getPath()) : theOSystem->createLauncher();
if(!launcherOpened)
{
theOSystem->logMessage("Launcher could not be started, showing usage", 2);
theOSystem->settings().usage();
return Cleanup();
}
}
else
{
const string& result = theOSystem->createConsole(romnode);
if(result != EmptyString)
return Cleanup();
if(theOSystem->settings().getBool("takesnapshot"))
{
theOSystem->logMessage("Taking snapshots with 'takesnapshot' ...", 2);
for(int i = 0; i < 30; ++i) theOSystem->frameBuffer().update();
theOSystem->eventHandler().takeSnapshot();
return Cleanup();
}
#ifdef DEBUGGER_SUPPORT
// Set up any breakpoint that was on the command line
// (and remove the key from the settings, so they won't get set again)
const string& initBreak = theOSystem->settings().getString("break");
if(initBreak != "")
{
Debugger& dbg = theOSystem->debugger();
int bp = dbg.stringToValue(initBreak);
dbg.setBreakPoint(bp, true);
theOSystem->settings().setValue("break", "");
}
#endif
}
// Swallow any spurious events in the queue
// These are normally caused by joystick/mouse jitter
SDL_Event event;
while(SDL_PollEvent(&event)) /* swallow event */ ;
// Start the main loop, and don't exit until the user issues a QUIT command
theOSystem->logMessage("Starting main loop ...", 2);
theOSystem->mainLoop();
theOSystem->logMessage("Finished main loop ...", 2);
// Cleanup time ...
return Cleanup();
}
+21
View File
@@ -0,0 +1,21 @@
MODULE := src/common
MODULE_OBJS := \
src/common/mainSDL.o \
src/common/Base.o \
src/common/SoundSDL.o \
src/common/FrameBufferSoft.o \
src/common/FrameBufferGL.o \
src/common/FBSurfaceGL.o \
src/common/FBSurfaceTIA.o \
src/common/FSNodeZIP.o \
src/common/PNGLibrary.o \
src/common/MouseControl.o \
src/common/RectList.o \
src/common/ZipHandler.o
MODULE_DIRS += \
src/common
# Include common rules
include $(srcdir)/common.rules
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

+38
View File
@@ -0,0 +1,38 @@
/* XPM */
static const char * stella_icon[] = {
"32 32 3 1",
" c None",
". c #000000",
"+ c #FFFFFF",
" ",
" ",
" ",
" ",
" .............. ",
" .............. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ....++..++..++.... ",
" ....++..++..++.... ",
" ....++++..++..++++.... ",
" ....++++..++..++++.... ",
" ..++++....++....++++.. ",
" ..++++....++....++++.. ",
" ......++......++......++...... ",
" ......++......++......++...... ",
" ..++++++.. ..++.. ..++++++.. ",
" ..++++++.. ..++.. ..++++++.. ",
" ..++++.... ..++.. ....++++.. ",
" ..++++.... ..++.. ....++++.. ",
" ........ ...... ........ ",
" ........ ...... ........ ",
" ",
" ",
" ",
" "};
+291
View File
@@ -0,0 +1,291 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: NTSCFilter.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "FrameBuffer.hxx"
#include "Settings.hxx"
#include "NTSCFilter.hxx"
#define SCALE_FROM_100(x) ((x/50.0)-1.0)
#define SCALE_TO_100(x) (uInt32)(50*(x+1.0))
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NTSCFilter::NTSCFilter()
: mySetup(atari_ntsc_composite),
myPreset(PRESET_OFF),
myCurrentAdjustable(0)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NTSCFilter::~NTSCFilter()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::setTIAPalette(const FrameBuffer& fb, const uInt32* palette)
{
// Normal TIA palette contains 256 colours, where every odd indexed colour
// is used for PAL colour-loss effect
// This can't be emulated here, since the memory requirements would be too
// great (a 4x increase)
// Therefore, we need to skip every second index, since the array passed to
// the Blargg code assumes 128 colours
uInt8* ptr = myTIAPalette;
// Set palette for phosphor effect
for(int i = 0; i < 256; i+=2)
{
for(int j = 0; j < 256; j+=2)
{
uInt8 ri = (palette[i] >> 16) & 0xff;
uInt8 gi = (palette[i] >> 8) & 0xff;
uInt8 bi = palette[i] & 0xff;
uInt8 rj = (palette[j] >> 16) & 0xff;
uInt8 gj = (palette[j] >> 8) & 0xff;
uInt8 bj = palette[j] & 0xff;
*ptr++ = fb.getPhosphor(ri, rj);
*ptr++ = fb.getPhosphor(gi, gj);
*ptr++ = fb.getPhosphor(bi, bj);
}
}
// Set palette for normal fill
for(int i = 0; i < 256; i+=2)
{
*ptr++ = (palette[i] >> 16) & 0xff;
*ptr++ = (palette[i] >> 8) & 0xff;
*ptr++ = palette[i] & 0xff;
}
updateFilter();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string NTSCFilter::setPreset(Preset preset)
{
myPreset = preset;
string msg = "disabled";
switch(myPreset)
{
case PRESET_COMPOSITE:
mySetup = atari_ntsc_composite;
msg = "COMPOSITE";
break;
case PRESET_SVIDEO:
mySetup = atari_ntsc_svideo;
msg = "S-VIDEO";
break;
case PRESET_RGB:
mySetup = atari_ntsc_rgb;
msg = "RGB";
break;
case PRESET_BAD:
mySetup = atari_ntsc_bad;
msg = "BAD ADJUST";
break;
case PRESET_CUSTOM:
mySetup = myCustomSetup;
msg = "CUSTOM";
break;
default:
return msg;
}
updateFilter();
return msg;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string NTSCFilter::getPreset() const
{
switch(myPreset)
{
case PRESET_COMPOSITE: return "COMPOSITE";
case PRESET_SVIDEO: return "S-VIDEO";
case PRESET_RGB: return "RGB";
case PRESET_BAD: return "BAD ADJUST";
case PRESET_CUSTOM: return "CUSTOM";
default: return "Disabled";
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string NTSCFilter::setNextAdjustable()
{
if(myPreset != PRESET_CUSTOM)
return "'Custom' TV mode not selected";
myCurrentAdjustable = (myCurrentAdjustable + 1) % 10;
ostringstream buf;
buf << "Custom adjustable '" << ourCustomAdjustables[myCurrentAdjustable].type
<< "' selected";
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string NTSCFilter::setPreviousAdjustable()
{
if(myPreset != PRESET_CUSTOM)
return "'Custom' TV mode not selected";
if(myCurrentAdjustable == 0) myCurrentAdjustable = 9;
else myCurrentAdjustable--;
ostringstream buf;
buf << "Custom adjustable '" << ourCustomAdjustables[myCurrentAdjustable].type
<< "' selected";
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string NTSCFilter::increaseAdjustable()
{
if(myPreset != PRESET_CUSTOM)
return "'Custom' TV mode not selected";
uInt32 newval = SCALE_TO_100(*ourCustomAdjustables[myCurrentAdjustable].value);
newval += 2; if(newval > 100) newval = 100;
*ourCustomAdjustables[myCurrentAdjustable].value = SCALE_FROM_100(newval);
ostringstream buf;
buf << "Custom '" << ourCustomAdjustables[myCurrentAdjustable].type
<< "' set to " << newval;
setPreset(myPreset);
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string NTSCFilter::decreaseAdjustable()
{
if(myPreset != PRESET_CUSTOM)
return "'Custom' TV mode not selected";
uInt32 newval = SCALE_TO_100(*ourCustomAdjustables[myCurrentAdjustable].value);
if(newval < 2) newval = 0;
else newval -= 2;
*ourCustomAdjustables[myCurrentAdjustable].value = SCALE_FROM_100(newval);
ostringstream buf;
buf << "Custom '" << ourCustomAdjustables[myCurrentAdjustable].type
<< "' set to " << newval;
setPreset(myPreset);
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::loadConfig(const Settings& settings)
{
// Load adjustables for custom mode
myCustomSetup.hue = BSPF_clamp(settings.getFloat("tv_hue"), -1.0f, 1.0f);
myCustomSetup.saturation = BSPF_clamp(settings.getFloat("tv_saturation"), -1.0f, 1.0f);
myCustomSetup.contrast = BSPF_clamp(settings.getFloat("tv_contrast"), -1.0f, 1.0f);
myCustomSetup.brightness = BSPF_clamp(settings.getFloat("tv_brightness"), -1.0f, 1.0f);
myCustomSetup.sharpness = BSPF_clamp(settings.getFloat("tv_sharpness"), -1.0f, 1.0f);
myCustomSetup.gamma = BSPF_clamp(settings.getFloat("tv_gamma"), -1.0f, 1.0f);
myCustomSetup.resolution = BSPF_clamp(settings.getFloat("tv_resolution"), -1.0f, 1.0f);
myCustomSetup.artifacts = BSPF_clamp(settings.getFloat("tv_artifacts"), -1.0f, 1.0f);
myCustomSetup.fringing = BSPF_clamp(settings.getFloat("tv_fringing"), -1.0f, 1.0f);
myCustomSetup.bleed = BSPF_clamp(settings.getFloat("tv_bleed"), -1.0f, 1.0f);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::saveConfig(Settings& settings) const
{
// Save adjustables for custom mode
settings.setValue("tv_hue", myCustomSetup.hue);
settings.setValue("tv_saturation", myCustomSetup.saturation);
settings.setValue("tv_contrast", myCustomSetup.contrast);
settings.setValue("tv_brightness", myCustomSetup.brightness);
settings.setValue("tv_sharpness", myCustomSetup.sharpness);
settings.setValue("tv_gamma", myCustomSetup.gamma);
settings.setValue("tv_resolution", myCustomSetup.resolution);
settings.setValue("tv_artifacts", myCustomSetup.artifacts);
settings.setValue("tv_fringing", myCustomSetup.fringing);
settings.setValue("tv_bleed", myCustomSetup.bleed);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset)
{
switch(preset)
{
case PRESET_COMPOSITE:
convertToAdjustable(adjustable, atari_ntsc_composite); break;
case PRESET_SVIDEO:
convertToAdjustable(adjustable, atari_ntsc_svideo); break;
case PRESET_RGB:
convertToAdjustable(adjustable, atari_ntsc_rgb); break;
case PRESET_BAD:
convertToAdjustable(adjustable, atari_ntsc_bad); break;
case PRESET_CUSTOM:
convertToAdjustable(adjustable, myCustomSetup); break;
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::setCustomAdjustables(Adjustable& adjustable)
{
myCustomSetup.hue = SCALE_FROM_100(adjustable.hue);
myCustomSetup.saturation = SCALE_FROM_100(adjustable.saturation);
myCustomSetup.contrast = SCALE_FROM_100(adjustable.contrast);
myCustomSetup.brightness = SCALE_FROM_100(adjustable.brightness);
myCustomSetup.sharpness = SCALE_FROM_100(adjustable.sharpness);
myCustomSetup.gamma = SCALE_FROM_100(adjustable.gamma);
myCustomSetup.resolution = SCALE_FROM_100(adjustable.resolution);
myCustomSetup.artifacts = SCALE_FROM_100(adjustable.artifacts);
myCustomSetup.fringing = SCALE_FROM_100(adjustable.fringing);
myCustomSetup.bleed = SCALE_FROM_100(adjustable.bleed);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::convertToAdjustable(Adjustable& adjustable,
const atari_ntsc_setup_t& setup) const
{
adjustable.hue = SCALE_TO_100(setup.hue);
adjustable.saturation = SCALE_TO_100(setup.saturation);
adjustable.contrast = SCALE_TO_100(setup.contrast);
adjustable.brightness = SCALE_TO_100(setup.brightness);
adjustable.sharpness = SCALE_TO_100(setup.sharpness);
adjustable.gamma = SCALE_TO_100(setup.gamma);
adjustable.resolution = SCALE_TO_100(setup.resolution);
adjustable.artifacts = SCALE_TO_100(setup.artifacts);
adjustable.fringing = SCALE_TO_100(setup.fringing);
adjustable.bleed = SCALE_TO_100(setup.bleed);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
atari_ntsc_setup_t NTSCFilter::myCustomSetup = atari_ntsc_composite;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const NTSCFilter::AdjustableTag NTSCFilter::ourCustomAdjustables[10] = {
{ "contrast", &myCustomSetup.contrast },
{ "brightness", &myCustomSetup.brightness },
{ "hue", &myCustomSetup.hue },
{ "saturation", &myCustomSetup.saturation },
{ "gamma", &myCustomSetup.gamma },
{ "sharpness", &myCustomSetup.sharpness },
{ "resolution", &myCustomSetup.resolution },
{ "artifacts", &myCustomSetup.artifacts },
{ "fringing", &myCustomSetup.fringing },
{ "bleeding", &myCustomSetup.bleed }
};
+165
View File
@@ -0,0 +1,165 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: NTSCFilter.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef NTSC_FILTER_HXX
#define NTSC_FILTER_HXX
class FrameBuffer;
class Settings;
#include "bspf.hxx"
#include "atari_ntsc.h"
/**
This class is based on the Blargg NTSC filter code from Atari800,
and is derived from 'filter_ntsc.(h|c)'. Original code based on
implementation from http://www.slack.net/~ant.
The class is basically a thin wrapper around atari_ntsc_xxx structs
and methods, so that the rest of the codebase isn't affected by
updated versions of Blargg code.
*/
class NTSCFilter
{
public:
NTSCFilter();
virtual ~NTSCFilter();
public:
// Set one of the available preset adjustments (Composite, S-Video, RGB, etc)
enum Preset {
PRESET_OFF,
PRESET_COMPOSITE,
PRESET_SVIDEO,
PRESET_RGB,
PRESET_BAD,
PRESET_CUSTOM
};
/* Normally used in conjunction with custom mode, contains all
aspects currently adjustable in NTSC TV emulation. */
struct Adjustable {
uInt32 hue, saturation, contrast, brightness, gamma,
sharpness, resolution, artifacts, fringing, bleed;
};
public:
/* Informs the NTSC filter about the current TIA palette. The filter
uses this as a baseline for calculating its own internal palette
in YIQ format.
*/
void setTIAPalette(const FrameBuffer& fb, const uInt32* palette);
// The following are meant to be used strictly for toggling from the GUI
string setPreset(Preset preset);
// Get current preset info encoded as a string
string getPreset() const;
// Reinitialises the NTSC filter (automatically called after settings
// have changed)
inline void updateFilter()
{
atari_ntsc_init(&myFilter, &mySetup, myTIAPalette);
}
// Get adjustables for the given preset
// Values will be scaled to 0 - 100 range, independent of how
// they're actually stored internally
void getAdjustables(Adjustable& adjustable, Preset preset);
// Set custom adjustables to given values
// Values will be scaled to 0 - 100 range, independent of how
// they're actually stored internally
void setCustomAdjustables(Adjustable& adjustable);
// The following methods cycle through each custom adjustable
// They are used in conjunction with the increase/decrease
// methods, which change the currently selected adjustable
// Changes are made this way since otherwise 20 key-combinations
// would be needed to dynamically change each setting, and now
// only 4 combinations are necessary
string setNextAdjustable();
string setPreviousAdjustable();
string increaseAdjustable();
string decreaseAdjustable();
// Load and save NTSC-related settings
void loadConfig(const Settings& settings);
void saveConfig(Settings& settings) const;
// Perform Blargg filtering on input buffer, place results in
// output buffer
// In the current implementation, the source pitch is always the
// same as the actual width
inline void blit_single(uInt8* src_buf, int src_width, int src_height,
uInt32* dest_buf, long dest_pitch)
{
atari_ntsc_blit_single(&myFilter, src_buf, src_width, src_width, src_height,
dest_buf, dest_pitch);
}
inline void blit_double(uInt8* src_buf, uInt8* src_back_buf,
int src_width, int src_height,
uInt32* dest_buf, long dest_pitch)
{
atari_ntsc_blit_double(&myFilter, src_buf, src_back_buf, src_width, src_width,
src_height, dest_buf, dest_pitch);
}
private:
// Convert from atari_ntsc_setup_t values to equivalent adjustables
void convertToAdjustable(Adjustable& adjustable,
const atari_ntsc_setup_t& setup) const;
private:
// The NTSC filter structure
atari_ntsc_t myFilter;
// Contains controls used to adjust the palette in the NTSC filter
// This is the main setup object used by the underlying ntsc code
atari_ntsc_setup_t mySetup;
// This setup is used only in custom mode (after it is modified,
// it is copied to mySetup)
static atari_ntsc_setup_t myCustomSetup;
// Current preset in use
Preset myPreset;
// The base 2600 palette contains 128 colours
// However, 'phosphor' mode needs a 128x128 matrix to simulate
// low-flicker output, so we need 128x128 + 128, or 129x128
// Note that this is a huge hack, which hopefully will go
// away once the phosphor effect can be more properly emulated
// Memory layout is as follows:
//
// 128x128 in first bytes of array
// 128 in last bytes of array
// Each colour is represented by 3 bytes, in R,G,B order
uInt8 myTIAPalette[atari_ntsc_palette_size * 3];
struct AdjustableTag {
const char* type;
double* value;
};
uInt32 myCurrentAdjustable;
static const AdjustableTag ourCustomAdjustables[10];
};
#endif
+212
View File
@@ -0,0 +1,212 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: atari_ntsc.c 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "atari_ntsc.h"
/* Copyright (C) 2006-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
*/
atari_ntsc_setup_t const atari_ntsc_composite = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.15, 0.0, 0.0, 0.0, 0 };
atari_ntsc_setup_t const atari_ntsc_svideo = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.45, -1.0, -1.0, 0.0, 0 };
atari_ntsc_setup_t const atari_ntsc_rgb = { 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.70, -1.0, -1.0, -1.0, 0 };
atari_ntsc_setup_t const atari_ntsc_bad = { 0.1, -0.3, 0.3, 0.25, 0.2, 0.0, 0.1, 0.5, 0.5, 0.5, 0 };
#define alignment_count 2
#define burst_count 1
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.5f
#define artifacts_max 2.5f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define gamma_size 256
#include "atari_ntsc_impl.h"
/* 2 input pixels -> 8 composite samples */
pixel_info_t const atari_ntsc_pixels [alignment_count] = {
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, 1, 1 } },
{ PIXEL_OFFSET( 0, -5 ), { 1, 1, 1, 1 } },
};
static void correct_errors( atari_ntsc_rgb_t color, atari_ntsc_rgb_t* out )
{
unsigned i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
atari_ntsc_rgb_t error = color -
out [i ] - out [(i+10)%14+14] -
out [i + 7] - out [i + 3 +14];
CORRECT_ERROR( i + 3 + 14 );
}
}
void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup,
atari_ntsc_in_t const* palette )
{
int entry;
init_t impl;
if ( !setup )
setup = &atari_ntsc_composite;
init( &impl, setup );
// Palette stores R/G/B data for 'atari_ntsc_palette_size' entries
for ( entry = 0; entry < atari_ntsc_palette_size; entry++ )
{
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 );
// Generate kernel
int ir, ig, ib = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, ir, ig );
atari_ntsc_rgb_t rgb = PACK_RGB( ir, ig, ib );
if ( ntsc )
{
atari_ntsc_rgb_t* kernel = ntsc->table [entry];
gen_kernel( &impl, y, i, q, kernel );
correct_errors( rgb, kernel );
}
}
}
void atari_ntsc_blit_single( atari_ntsc_t const* ntsc,
atari_ntsc_in_t const* atari_in,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch )
{
#define TO_SINGLE(pixel) ((1<<14)+(pixel>>1))
int const chunk_count = (in_width - 1) / atari_ntsc_in_chunk;
while ( in_height-- )
{
atari_ntsc_in_t const* line_in = atari_in;
ATARI_NTSC_BEGIN_ROW( ntsc, TO_SINGLE(atari_ntsc_black), TO_SINGLE(line_in[0]) );
atari_ntsc_out_t* restrict line_out = (atari_ntsc_out_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ntsc, TO_SINGLE(line_in[0]) );
ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] );
ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] );
ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] );
ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] );
ATARI_NTSC_COLOR_IN( 1, ntsc, TO_SINGLE(line_in[1]) );
ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] );
ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] );
ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] );
line_in += 2;
line_out += 7;
}
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, ntsc, TO_SINGLE(atari_ntsc_black) );
ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] );
ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] );
ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] );
ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] );
ATARI_NTSC_COLOR_IN( 1, ntsc, TO_SINGLE(atari_ntsc_black) );
ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] );
ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] );
ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] );
atari_in += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void atari_ntsc_blit_double( atari_ntsc_t const* ntsc,
atari_ntsc_in_t const* atari_in1, atari_ntsc_in_t const* atari_in2,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch )
{
#define TO_DOUBLE(pixel1, pixel2) (((pixel1>>1)<<7)+(pixel2>>1))
int const chunk_count = (in_width - 1) / atari_ntsc_in_chunk;
while ( in_height-- )
{
atari_ntsc_in_t const* line_in1 = atari_in1;
atari_ntsc_in_t const* line_in2 = atari_in2;
ATARI_NTSC_BEGIN_ROW( ntsc,
TO_DOUBLE(atari_ntsc_black, atari_ntsc_black),
TO_DOUBLE(line_in1[0], line_in2[0]) );
atari_ntsc_out_t* restrict line_out = (atari_ntsc_out_t*) rgb_out;
int n;
++line_in1;
++line_in2;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ntsc,
TO_DOUBLE(line_in1[0], line_in2[0]) );
ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] );
ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] );
ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] );
ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] );
ATARI_NTSC_COLOR_IN( 1, ntsc,
TO_DOUBLE(line_in1[1], line_in2[1]) );
ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] );
ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] );
ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] );
line_in1 += 2;
line_in2 += 2;
line_out += 7;
}
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, ntsc,
TO_DOUBLE(atari_ntsc_black, atari_ntsc_black) );
ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] );
ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] );
ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] );
ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] );
ATARI_NTSC_COLOR_IN( 1, ntsc,
TO_DOUBLE(atari_ntsc_black, atari_ntsc_black) );
ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] );
ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] );
ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] );
atari_in1 += in_row_width;
atari_in2 += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
+159
View File
@@ -0,0 +1,159 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: atari_ntsc.h 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
/* Atari TIA, CTIA, GTIA and MARIA NTSC video filter */
#ifndef ATARI_NTSC_H
#define ATARI_NTSC_H
typedef unsigned char atari_ntsc_in_t;
typedef unsigned int atari_ntsc_out_t;
#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 atari_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) */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
} atari_ntsc_setup_t;
/* Video format presets */
extern atari_ntsc_setup_t const atari_ntsc_composite; /* color bleeding + artifacts */
extern atari_ntsc_setup_t const atari_ntsc_svideo; /* color bleeding only */
extern atari_ntsc_setup_t const atari_ntsc_rgb; /* crisp image */
extern atari_ntsc_setup_t const atari_ntsc_bad; /* badly adjusted TV */
enum { atari_ntsc_palette_size = 129 * 128 };
/* Initializes and adjusts parameters. Can be called multiple times on the same
atari_ntsc_t object. Can pass NULL for either parameter. */
typedef struct atari_ntsc_t atari_ntsc_t;
void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup,
atari_ntsc_in_t const* palette );
/* Filters one or more rows of pixels. Input pixels are 8-bit Atari palette colors.
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. */
void atari_ntsc_blit_single( atari_ntsc_t const* ntsc,
atari_ntsc_in_t const* atari_in,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch );
void atari_ntsc_blit_double( atari_ntsc_t const* ntsc,
atari_ntsc_in_t const* atari_in1, atari_ntsc_in_t const* atari_in2,
long in_row_width, 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 ATARI_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 160 down at all. */
#define ATARI_NTSC_OUT_WIDTH( in_width ) \
((((in_width) - 1) / atari_ntsc_in_chunk + 1)* atari_ntsc_out_chunk)
/* Number of input pixels that will fit within given output width. Might be
rounded down slightly; use ATARI_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define ATARI_NTSC_IN_WIDTH( out_width ) \
(((out_width) / atari_ntsc_out_chunk - 1) * atari_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters. */
enum { atari_ntsc_in_chunk = 2 }; /* number of input pixels read per chunk */
enum { atari_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { atari_ntsc_black = 0 }; /* palette index for black */
/* Begins outputting row and starts two pixels. First pixel will be cut off a bit.
Use atari_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
#define ATARI_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1 ) \
ATARI_NTSC_BEGIN_ROW_6_( pixel0, pixel1, ATARI_NTSC_ENTRY_, ntsc )
/* Begins input pixel */
#define ATARI_NTSC_COLOR_IN( in_index, ntsc, color_in ) \
ATARI_NTSC_COLOR_IN_( in_index, color_in, ATARI_NTSC_ENTRY_, ntsc )
/* Generates output in the specified 32-bit format (x = junk bits).
native: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format)
8888: 00000000 RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8-8 32-bit ARGB)
*/
#define ATARI_NTSC_RGB_OUT_8888( index, rgb_out ) {\
atari_ntsc_rgb_t raw_ =\
kernel0 [index ] + kernel1 [(index+10)%7+14] +\
kernelx0 [(index+7)%14] + kernelx1 [(index+ 3)%7+14+7];\
ATARI_NTSC_CLAMP_( raw_, 0 );\
rgb_out = (raw_>>5 & 0x00FF0000)|(raw_>>3 & 0x0000FF00)|(raw_>>1 & 0x000000FF);\
}
/* private */
enum { atari_ntsc_entry_size = 2 * 14 };
typedef unsigned long atari_ntsc_rgb_t;
struct atari_ntsc_t {
atari_ntsc_rgb_t table [atari_ntsc_palette_size] [atari_ntsc_entry_size];
};
#define ATARI_NTSC_ENTRY_( ntsc, n ) (ntsc)->table [n]
/* common 3->7 ntsc macros */
#define ATARI_NTSC_BEGIN_ROW_6_( pixel0, pixel1, ENTRY, table ) \
unsigned const atari_ntsc_pixel0_ = (pixel0);\
atari_ntsc_rgb_t const* kernel0 = ENTRY( table, atari_ntsc_pixel0_ );\
unsigned const atari_ntsc_pixel1_ = (pixel1);\
atari_ntsc_rgb_t const* kernel1 = ENTRY( table, atari_ntsc_pixel1_ );\
atari_ntsc_rgb_t const* kernelx0;\
atari_ntsc_rgb_t const* kernelx1 = kernel0
/* common ntsc macros */
#define atari_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
#define atari_ntsc_clamp_mask (atari_ntsc_rgb_builder * 3 / 2)
#define atari_ntsc_clamp_add (atari_ntsc_rgb_builder * 0x101)
#define ATARI_NTSC_CLAMP_( io, shift ) {\
atari_ntsc_rgb_t sub = (io) >> (9-(shift)) & atari_ntsc_clamp_mask;\
atari_ntsc_rgb_t clamp = atari_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define ATARI_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
unsigned color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
}
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,437 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: atari_ntsc_impl.h 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
/* Based on nes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* Copyright (C) 2006-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 */
#ifndef DISABLE_CORRECTION
#define DISABLE_CORRECTION 0
#endif
#undef PI
#define PI 3.14159265358979323846f
#ifndef LUMA_CUTOFF
#define LUMA_CUTOFF 0.20
#endif
#ifndef gamma_size
#define gamma_size 1
#endif
#ifndef rgb_bits
#define rgb_bits 8
#endif
#ifndef artifacts_max
#define artifacts_max (artifacts_mid * 1.5f)
#endif
#ifndef fringing_max
#define fringing_max (fringing_mid * 2)
#endif
#ifndef STD_HUE_CONDITION
#define STD_HUE_CONDITION( setup ) 1
#endif
#define ext_decoder_hue (std_decoder_hue + 15)
#define rgb_unit (1 << rgb_bits)
#define rgb_offset (rgb_unit * 2 + 0.5f)
enum { burst_size = atari_ntsc_entry_size / burst_count };
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
typedef struct init_t
{
float to_rgb [burst_count * 6];
float to_float [gamma_size];
float contrast;
float brightness;
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
} init_t;
#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_filters( init_t* impl, atari_ntsc_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;
int n = rescale_out;
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 ( --n );
}
#endif
}
static float const default_decoder [6] =
{ 0.9563f, 0.6210f, -0.2721f, -0.6474f, -1.1070f, 1.7046f };
static void init( init_t* impl, atari_ntsc_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_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;
if ( STD_HUE_CONDITION( setup ) )
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.595716f - (g) * 0.274453f - (b) * 0.321263f),\
((r) * 0.211456f - (g) * 0.522591f + (b) * 0.311135f)\
)
#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)\
)
#ifndef PACK_RGB
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
#endif
enum { rgb_kernel_size = burst_size / alignment_count };
enum { rgb_bias = rgb_unit * 2 * atari_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 atari_ntsc_pixels [alignment_count];
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( init_t* impl, float y, float i, float q, atari_ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
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 = atari_ntsc_pixels;
int alignment_remain = alignment_count;
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;
++pixel;
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 - 1;
else
k -= kernel_size * 2 * (rescale_out - 1) + 2;
{
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
}
}
}
while ( alignment_count > 1 && --alignment_remain );
if ( burst_count <= 1 )
break;
to_rgb += 6;
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
}
while ( --burst_remain );
}
static void correct_errors( atari_ntsc_rgb_t color, atari_ntsc_rgb_t* out );
#if DISABLE_CORRECTION
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c ) {\
atari_ntsc_rgb_t fourth = (error + 2 * atari_ntsc_rgb_builder) >> 2;\
fourth &= (rgb_bias >> 1) - atari_ntsc_rgb_builder;\
fourth -= rgb_bias >> 2;\
out [a] += fourth;\
out [b] += fourth;\
out [c] += fourth;\
out [i] += error - (fourth * 3);\
}
#endif
#define RGB_PALETTE_OUT( rgb, out_ )\
{\
unsigned char* out = (out_);\
atari_ntsc_rgb_t clamped = (rgb);\
ATARI_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
out [0] = (unsigned char) (clamped >> 21);\
out [1] = (unsigned char) (clamped >> 11);\
out [2] = (unsigned char) (clamped >> 1);\
}
/* blitter related */
#ifndef restrict
#if defined (__GNUC__)
#define restrict __restrict__
#elif defined (_MSC_VER) && _MSC_VER > 1300
#define restrict __restrict
#else
/* no support for restricted pointers */
#define restrict
#endif
#endif
+11
View File
@@ -0,0 +1,11 @@
MODULE := src/common/tv_filters
MODULE_OBJS := \
src/common/tv_filters/NTSCFilter.o \
src/common/tv_filters/atari_ntsc.o
MODULE_DIRS += \
src/common/tv_filters
# Include common rules
include $(srcdir)/common.rules
File diff suppressed because it is too large Load Diff
+406
View File
@@ -0,0 +1,406 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CartDebug.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CART_DEBUG_HXX
#define CART_DEBUG_HXX
class Settings;
class CartDebugWidget;
#include <map>
#include <set>
#include <list>
#include "bspf.hxx"
#include "Array.hxx"
#include "Base.hxx"
#include "Cart.hxx"
#include "DebuggerSystem.hxx"
#include "System.hxx"
// pointer types for CartDebug instance methods
class CartDebug;
typedef int (CartDebug::*CARTDEBUG_INT_METHOD)();
// call the pointed-to method on the (global) CPU debugger object.
#define CALL_CARTDEBUG_METHOD(method) ( ( Debugger::debugger().cartDebug().*method)() )
class CartState : public DebuggerState
{
public:
IntArray ram; // The actual data values
IntArray rport; // Address for reading from RAM
IntArray wport; // Address for writing to RAM
string bank; // Current banking layout
};
class CartDebug : public DebuggerSystem
{
// The disassembler needs special access to this class
friend class DiStella;
public:
enum DisasmType {
NONE = 0,
REFERENCED = 1 << 0, /* code somewhere in the program references it,
i.e. LDA $F372 referenced $F372 */
VALID_ENTRY = 1 << 1, /* addresses that can have a label placed in front of it.
A good counterexample would be "FF00: LDA $FE00"; $FF01
would be in the middle of a multi-byte instruction, and
therefore cannot be labelled. */
// The following correspond to specific types that can be set within the
// debugger, or specified in a Distella cfg file, and are listed in order
// of decreasing hierarchy
//
CODE = 1 << 7, // disassemble-able code segments
TCODE = 1 << 6, // (tentative) disassemble-able code segments
GFX = 1 << 5, // addresses loaded into GRPx registers
PGFX = 1 << 4, // addresses loaded into PFx registers
DATA = 1 << 3, // addresses loaded into registers other than GRPx / PFx
ROW = 1 << 2 // all other addresses
};
struct DisassemblyTag {
DisasmType type;
uInt16 address;
string label;
string disasm;
string ccount;
string bytes;
bool hllabel;
};
typedef Common::Array<DisassemblyTag> DisassemblyList;
struct Disassembly {
DisassemblyList list;
int fieldwidth;
};
public:
CartDebug(Debugger& dbg, Console& console, const OSystem& osystem);
virtual ~CartDebug();
const DebuggerState& getState();
const DebuggerState& getOldState() { return myOldState; }
void saveOldState();
string toString();
// Used to get/set the debug widget, which contains cart-specific
// functionality
CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; }
// The following assume that the given addresses are using the
// correct read/write port ranges; no checking will be done to
// confirm this.
uInt8 peek(uInt16 addr) { return mySystem.peek(addr); }
uInt16 dpeek(uInt16 addr) { return mySystem.peek(addr) | (mySystem.peek(addr+1) << 8); }
void poke(uInt16 addr, uInt8 value) { mySystem.poke(addr, value); }
// Indicate that a read from write port has occurred at the specified
// address.
void triggerReadFromWritePort(uInt16 address);
// Return the address at which an invalid read was performed in a
// write port area.
int readFromWritePort();
/**
Let the Cart debugger subsystem treat this area as addressable memory.
@param start The beginning of the RAM area (0x0000 - 0x2000)
@param size Total number of bytes of area
@param roffset Offset to use when reading from RAM (read port)
@param woffset Offset to use when writing to RAM (write port)
*/
void addRamArea(uInt16 start, uInt16 size, uInt16 roffset, uInt16 woffset);
// The following two methods are meant to be used together
// First, a call is made to disassemble(), which updates the disassembly
// list; it will figure out when an actual complete disassembly is
// required, and when the previous results can be used
//
// Later, successive calls to disassemblyList() simply return the
// previous results; no disassembly is done in this case
/**
Disassemble from the given address using the Distella disassembler
Address-to-label mappings (and vice-versa) are also determined here
@param force Force a re-disassembly, even if the state hasn't changed
@return True if disassembly changed from previous call, else false
*/
bool disassemble(bool force = false);
/**
Get the results from the most recent call to disassemble()
*/
const Disassembly& disassembly() const { return myDisassembly; }
/**
Determine the line in the disassembly that corresponds to the given address.
@param address The address to search for
@return Line number of the address, else -1 if no such address exists
*/
int addressToLine(uInt16 address) const;
/**
Disassemble from the starting address the specified number of lines.
Note that automatic code determination is turned off for this method;
@param start The start address for disassembly
@param lines The number of disassembled lines to generate
@return The disassembly represented as a string
*/
string disassemble(uInt16 start, uInt16 lines) const;
/**
Add a directive to the disassembler. Directives are basically overrides
for the automatic code determination algorithm in Distella, since some
things can't be automatically determined. For now, these directives
have exactly the same syntax as in a distella configuration file.
@param type Currently, CODE/DATA/GFX are supported
@param start The start address (inclusive) to mark with the given type
@param end The end address (inclusive) to mark with the given type
@param bank Bank to which these directive apply (0 indicated current bank)
@return True if directive was added, else false if it was removed
*/
bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end,
int bank = -1);
// The following are convenience methods that query the cartridge object
// for the desired information.
/**
Get the current bank in use by the cartridge.
*/
int getBank(); // non-const because of use in YaccParser
/**
Get the total number of banks supported by the cartridge.
*/
int bankCount() const;
/**
Get the name/type of the cartridge.
*/
string getCartType() const;
/**
Add a label and associated address.
Labels that reference either TIA or RIOT spaces will not be processed.
*/
bool addLabel(const string& label, uInt16 address);
/**
Remove the given label and its associated address.
Labels that reference either TIA or RIOT spaces will not be processed.
*/
bool removeLabel(const string& label);
/**
Accessor methods for labels and addresses
The mapping from address to label can be one-to-many (ie, an
address can have different labels depending on its context, and
whether its being read or written; if isRead is true, the context
is a read, else it's a write
If places is not -1 and a label hasn't been defined, return a
formatted hexidecimal address
*/
bool getLabel(ostream& buf, uInt16 addr, bool isRead, int places = -1) const;
string getLabel(uInt16 addr, bool isRead, int places = -1) const;
int getAddress(const string& label) const;
/**
Load constants from list file (as generated by DASM).
*/
string loadListFile();
/**
Load user equates from symbol file (as generated by DASM).
*/
string loadSymbolFile();
/**
Load/save Distella config files (Distella directives)
*/
string loadConfigFile();
string saveConfigFile();
/**
Save disassembly and ROM file
*/
string saveDisassembly();
string saveRom();
/**
Show Distella directives (both set by the user and determined by Distella)
for the given bank (or all banks, if no bank is specified).
*/
string listConfig(int bank = -1);
/**
Clear Distella directives (set by the user) for the given bank
(or all banks, if no bank is specified.)
*/
string clearConfig(int bank = -1);
/**
Methods used by the command parser for tab-completion
In this case, return completions from the equate list(s)
*/
void getCompletions(const char* in, StringList& list) const;
// Convert given address to corresponding disassembly type and append to buf
void addressTypeAsString(ostream& buf, uInt16 addr) const;
private:
typedef map<uInt16, string> AddrToLabel;
typedef map<string, uInt16> LabelToAddr;
// Determine 'type' of address (ie, what part of the system accessed)
enum AddrType {
ADDR_TIA,
ADDR_IO,
ADDR_ZPRAM,
ADDR_ROM
};
AddrType addressType(uInt16 addr) const;
struct DirectiveTag {
DisasmType type;
uInt16 start;
uInt16 end;
};
typedef list<uInt16> AddressList;
typedef list<DirectiveTag> DirectiveList;
struct BankInfo {
uInt16 start; // start of address space
uInt16 end; // end of address space
uInt16 offset; // ORG value
uInt16 size; // size of a bank (in bytes)
AddressList addressList; // addresses which PC has hit
DirectiveList directiveList; // overrides for automatic code determination
BankInfo() : start(0), end(0), offset(0), size(0) { }
#if 0
friend ostream& operator<<(ostream& os, const BankInfo& b)
{
os << "start=$" << HEX4 << b.start << ", end=$" << HEX4 << b.end
<< ", offset=$" << HEX4 << b.offset << ", size=" << dec << b.size
<< endl
<< "addrlist: ";
AddressList::const_iterator i;
for(i = b.addressList.begin(); i != b.addressList.end(); ++i)
os << HEX4 << *i << " ";
return os;
}
#endif
};
// Address type information determined by Distella
uInt8 myDisLabels[0x1000], myDisDirectives[0x1000];
// Information on equates used in the disassembly
struct ReservedEquates {
bool TIARead[16];
bool TIAWrite[64];
bool IOReadWrite[24];
bool ZPRAM[128];
AddrToLabel Label;
};
ReservedEquates myReserved;
// Actually call DiStella to fill the DisassemblyList structure
// Return whether the search address was actually in the list
bool fillDisassemblyList(BankInfo& bankinfo, uInt16 search);
// Analyze of bank of ROM, generating a list of Distella directives
// based on its disassembly
void getBankDirectives(ostream& buf, BankInfo& info) const;
// Get disassembly enum type from 'flags', taking precendence into account
DisasmType disasmTypeAbsolute(uInt8 flags) const;
// Convert disassembly enum type to corresponding string and append to buf
void disasmTypeAsString(ostream& buf, DisasmType type) const;
// Convert all disassembly types in 'flags' to corresponding string and
// append to buf
void disasmTypeAsString(ostream& buf, uInt8 flags) const;
private:
const OSystem& myOSystem;
CartState myState;
CartState myOldState;
CartDebugWidget* myDebugWidget;
// A complete record of relevant diassembly information for each bank
Common::Array<BankInfo> myBankInfo;
// Used for the disassembly display, and mapping from addresses
// to corresponding lines of text in that display
Disassembly myDisassembly;
map<uInt16, int> myAddrToLineList;
bool myAddrToLineIsROM;
// Mappings from label to address (and vice versa) for items
// defined by the user (either through a DASM symbol file or manually
// from the commandline in the debugger)
AddrToLabel myUserLabels;
LabelToAddr myUserAddresses;
// Mappings from label to address (and vice versa) for constants
// defined through a DASM lst file
AddrToLabel myUserCLabels;
// LabelToAddr myUserCAddresses;
// Mappings for labels to addresses for system-defined equates
// Because system equate addresses can have different names
// (depending on access in read vs. write mode), we can only create
// a mapping from labels to addresses; addresses to labels are
// handled differently
LabelToAddr mySystemAddresses;
// Holds address at which the most recent read from a write port
// occurred
uInt16 myRWPortAddress;
// The maximum length of all labels currently defined
uInt16 myLabelLength;
// Filenames to use for various I/O (currently these are hardcoded)
string myListFile, mySymbolFile, myCfgFile, myDisasmFile, myRomFile;
/// Table of instruction mnemonics
static const char* ourTIAMnemonicR[16]; // read mode
static const char* ourTIAMnemonicW[64]; // write mode
static const char* ourIOMnemonic[24];
static const char* ourZPMnemonic[128];
};
#endif
+193
View File
@@ -0,0 +1,193 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CpuDebug.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <sstream>
#include "Array.hxx"
#include "M6502.hxx"
#include "Debugger.hxx"
#include "CartDebug.hxx"
#include "TIADebug.hxx"
#include "CpuDebug.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CpuDebug::CpuDebug(Debugger& dbg, Console& console)
: DebuggerSystem(dbg, console),
my6502(mySystem.m6502())
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const DebuggerState& CpuDebug::getState()
{
myState.PC = my6502.PC;
myState.SP = my6502.SP;
myState.PS = my6502.PS();
myState.A = my6502.A;
myState.X = my6502.X;
myState.Y = my6502.Y;
myState.srcS = my6502.lastSrcAddressS();
myState.srcA = my6502.lastSrcAddressA();
myState.srcX = my6502.lastSrcAddressX();
myState.srcY = my6502.lastSrcAddressY();
Debugger::set_bits(myState.PS, myState.PSbits);
return myState;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::saveOldState()
{
myOldState.PC = my6502.PC;
myOldState.SP = my6502.SP;
myOldState.PS = my6502.PS();
myOldState.A = my6502.A;
myOldState.X = my6502.X;
myOldState.Y = my6502.Y;
myOldState.srcS = my6502.lastSrcAddressS();
myOldState.srcA = my6502.lastSrcAddressA();
myOldState.srcX = my6502.lastSrcAddressX();
myOldState.srcY = my6502.lastSrcAddressY();
Debugger::set_bits(myOldState.PS, myOldState.PSbits);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setPC(int pc)
{
my6502.PC = pc;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setSP(int sp)
{
my6502.SP = sp;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setPS(int ps)
{
my6502.PS(ps);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setA(int a)
{
my6502.A = a;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setX(int x)
{
my6502.X = x;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setY(int y)
{
my6502.Y = y;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setN(bool on)
{
my6502.N = on;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setV(bool on)
{
my6502.V = on;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setB(bool on)
{
// nop - B is always true
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setD(bool on)
{
my6502.D = on;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setI(bool on)
{
my6502.I = on;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setZ(bool on)
{
my6502.notZ = !on;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::setC(bool on)
{
my6502.C = on;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleN()
{
my6502.N = !my6502.N;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleV()
{
my6502.V = !my6502.V;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleB()
{
// nop - B is always true
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleD()
{
my6502.D = !my6502.D;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleI()
{
my6502.I = !my6502.I;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleZ()
{
my6502.notZ = !my6502.notZ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CpuDebug::toggleC()
{
my6502.C = !my6502.C;
}
+103
View File
@@ -0,0 +1,103 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CpuDebug.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CPU_DEBUG_HXX
#define CPU_DEBUG_HXX
class EquateList;
#include "Array.hxx"
#include "M6502.hxx"
#include "System.hxx"
#include "DebuggerSystem.hxx"
// pointer types for CpuDebug instance methods
typedef int (CpuDebug::*CPUDEBUG_INT_METHOD)();
// call the pointed-to method on the (global) CPU debugger object.
#define CALL_CPUDEBUG_METHOD(method) ( ( Debugger::debugger().cpuDebug().*method)() )
class CpuState : public DebuggerState
{
public:
int PC, SP, PS, A, X, Y;
int srcS, srcA, srcX, srcY;
BoolArray PSbits;
};
class CpuDebug : public DebuggerSystem
{
public:
CpuDebug(Debugger& dbg, Console& console);
const DebuggerState& getState();
const DebuggerState& getOldState() { return myOldState; }
void saveOldState();
string toString() { return ""; } // Not needed, since CPU stuff is always visible
// I know, we ain't supposed to do this...
M6502& m6502() { return mySystem.m6502(); }
int pc() { return mySystem.m6502().PC; }
int sp() { return mySystem.m6502().SP; }
int a() { return mySystem.m6502().A; }
int x() { return mySystem.m6502().X; }
int y() { return mySystem.m6502().Y; }
// These return int, not boolean!
int n() { return mySystem.m6502().N; }
int v() { return mySystem.m6502().V; }
int b() { return mySystem.m6502().B; }
int d() { return mySystem.m6502().D; }
int i() { return mySystem.m6502().I; }
int z() { return !mySystem.m6502().notZ; }
int c() { return mySystem.m6502().C; }
void setPC(int pc);
void setSP(int sp);
void setPS(int ps);
void setA(int a);
void setX(int x);
void setY(int y);
void setN(bool on);
void setV(bool on);
void setB(bool on);
void setD(bool on);
void setI(bool on);
void setZ(bool on);
void setC(bool on);
void toggleN();
void toggleV();
void toggleB();
void toggleD();
void toggleI();
void toggleZ();
void toggleC();
private:
M6502& my6502;
CpuState myState;
CpuState myOldState;
};
#endif
+775
View File
@@ -0,0 +1,775 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Debugger.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "bspf.hxx"
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include "Version.hxx"
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
#include "FSNode.hxx"
#include "Settings.hxx"
#include "DebuggerDialog.hxx"
#include "DebuggerParser.hxx"
#include "StateManager.hxx"
#include "Console.hxx"
#include "System.hxx"
#include "M6502.hxx"
#include "Cart.hxx"
#include "TIA.hxx"
#include "CartDebug.hxx"
#include "CartDebugWidget.hxx"
#include "CpuDebug.hxx"
#include "RiotDebug.hxx"
#include "TIADebug.hxx"
#include "TiaInfoWidget.hxx"
#include "TiaOutputWidget.hxx"
#include "TiaZoomWidget.hxx"
#include "EditTextWidget.hxx"
#include "RomWidget.hxx"
#include "Expression.hxx"
#include "PackedBitArray.hxx"
#include "YaccParser.hxx"
#include "Debugger.hxx"
Debugger* Debugger::myStaticDebugger = 0;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const char* builtin_functions[][3] = {
// { "name", "definition", "help text" }
// left joystick:
{ "_joy0left", "!(*SWCHA & $40)", "Left joystick moved left" },
{ "_joy0right", "!(*SWCHA & $80)", "Left joystick moved right" },
{ "_joy0up", "!(*SWCHA & $10)", "Left joystick moved up" },
{ "_joy0down", "!(*SWCHA & $20)", "Left joystick moved down" },
{ "_joy0button", "!(*INPT4 & $80)", "Left joystick button pressed" },
// right joystick:
{ "_joy1left", "!(*SWCHA & $04)", "Right joystick moved left" },
{ "_joy1right", "!(*SWCHA & $08)", "Right joystick moved right" },
{ "_joy1up", "!(*SWCHA & $01)", "Right joystick moved up" },
{ "_joy1down", "!(*SWCHA & $02)", "Right joystick moved down" },
{ "_joy1button", "!(*INPT5 & $80)", "Right joystick button pressed" },
// console switches:
{ "_select", "!(*SWCHB & $02)", "Game Select pressed" },
{ "_reset", "!(*SWCHB & $01)", "Game Reset pressed" },
{ "_color", "*SWCHB & $08", "Color/BW set to Color" },
{ "_bw", "!(*SWCHB & $08)", "Color/BW set to BW" },
{ "_diff0b", "!(*SWCHB & $40)", "Left diff. set to B (easy)" },
{ "_diff0a", "*SWCHB & $40", "Left diff. set to A (hard)" },
{ "_diff1b", "!(*SWCHB & $80)", "Right diff. set to B (easy)" },
{ "_diff1a", "*SWCHB & $80", "Right diff. set to A (hard)" },
// empty string marks end of list, do not remove
{ 0, 0, 0 }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Names are defined here, but processed in YaccParser
static const char* pseudo_registers[][2] = {
// { "name", "help text" }
{ "_bank", "Currently selected bank" },
{ "_rwport", "Address at which a read from a write port occurred" },
{ "_scan", "Current scanline count" },
{ "_fcount", "Number of frames since emulation started" },
{ "_cclocks", "Color clocks on current scanline" },
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" },
{ "_vblank", "Whether vertical blank is enabled (1 or 0)" },
// empty string marks end of list, do not remove
{ 0, 0 }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::Debugger(OSystem& osystem, Console& console)
: DialogContainer(&osystem),
myConsole(console),
mySystem(console.system()),
myDialog(NULL),
myParser(NULL),
myCartDebug(NULL),
myCpuDebug(NULL),
myRiotDebug(NULL),
myTiaDebug(NULL),
myBreakPoints(NULL),
myReadTraps(NULL),
myWriteTraps(NULL),
myWidth(DebuggerDialog::kSmallFontMinW),
myHeight(DebuggerDialog::kSmallFontMinH),
myRewindManager(NULL)
{
// Init parser
myParser = new DebuggerParser(*this, osystem.settings());
// Create debugger subsystems
myCpuDebug = new CpuDebug(*this, myConsole);
myCartDebug = new CartDebug(*this, myConsole, osystem);
myRiotDebug = new RiotDebug(*this, myConsole);
myTiaDebug = new TIADebug(*this, myConsole);
myBreakPoints = new PackedBitArray(0x10000);
myReadTraps = new PackedBitArray(0x10000);
myWriteTraps = new PackedBitArray(0x10000);
// Allow access to this object from any class
// Technically this violates pure OO programming, but since I know
// there will only be ever one instance of debugger in Stella,
// I don't care :)
myStaticDebugger = this;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::~Debugger()
{
delete myParser;
delete myCartDebug;
delete myCpuDebug;
delete myRiotDebug;
delete myTiaDebug;
delete myBreakPoints;
delete myReadTraps;
delete myWriteTraps;
delete myRewindManager;
myStaticDebugger = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::initialize()
{
// Get the dialog size
const GUI::Size& size = myOSystem->settings().getSize("dbg.res");
myWidth = BSPF_max(size.w, 0);
myHeight = BSPF_max(size.h, 0);
myWidth = BSPF_max(myWidth, (uInt32)DebuggerDialog::kSmallFontMinW);
myHeight = BSPF_max(myHeight, (uInt32)DebuggerDialog::kSmallFontMinH);
myOSystem->settings().setValue("dbg.res", GUI::Size(myWidth, myHeight));
delete myBaseDialog; myBaseDialog = myDialog = NULL;
myDialog = new DebuggerDialog(myOSystem, this, 0, 0, myWidth, myHeight);
myBaseDialog = myDialog;
myRewindManager = new RewindManager(*myOSystem, myDialog->rewindButton());
myCartDebug->setDebugWidget(&(myDialog->cartDebug()));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBInitStatus Debugger::initializeVideo()
{
string title = string("Stella ") + STELLA_VERSION + ": Debugger mode";
return myOSystem->frameBuffer().initialize(title, myWidth, myHeight);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::start(const string& message, int address)
{
if(myOSystem->eventHandler().enterDebugMode())
{
// This must be done *after* we enter debug mode,
// so the message isn't erased
ostringstream buf;
buf << message;
if(address > -1)
buf << Common::Base::HEX4 << address;
myDialog->message().setText(buf.str());
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::startWithFatalError(const string& message)
{
if(myOSystem->eventHandler().enterDebugMode())
{
// This must be done *after* we enter debug mode,
// so the dialog is properly shown
myDialog->showFatalMessage(message);
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::quit(bool exitrom)
{
if(exitrom)
myOSystem->eventHandler().handleEvent(Event::LauncherMode, 1);
else
myOSystem->eventHandler().leaveDebugMode();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Debugger::autoExec()
{
ostringstream buf;
// autoexec.stella is always run
FilesystemNode autoexec(myOSystem->baseDir() + "autoexec.stella");
buf << "autoExec():" << endl
<< myParser->exec(autoexec) << endl;
// Also, "romname.stella" if present
FilesystemNode romname(myOSystem->romFile().getPathWithExt(".stella"));
buf << myParser->exec(romname) << endl;
// Init builtins
for(int i = 0; builtin_functions[i][0] != 0; i++)
{
// TODO - check this for memory leaks
int res = YaccParser::parse(builtin_functions[i][1]);
if(res != 0) cerr << "ERROR in builtin function!" << endl;
Expression* exp = YaccParser::getResult();
addFunction(builtin_functions[i][0], builtin_functions[i][1], exp, true);
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::run(const string& command)
{
return myParser->run(command);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::invIfChanged(int reg, int oldReg)
{
string ret;
bool changed = reg != oldReg;
if(changed) ret += "\177";
ret += Common::Base::toString(reg, Common::Base::F_16_2);
if(changed) ret += "\177";
return ret;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::reset()
{
myCpuDebug->setPC(dpeek(0xfffc));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/* Element 0 of args is the address. The remaining elements are the data
to poke, starting at the given address.
*/
string Debugger::setRAM(IntArray& args)
{
ostringstream buf;
int count = args.size();
int address = args[0];
for(int i = 1; i < count; ++i)
mySystem.poke(address++, args[i]);
buf << "changed " << (count-1) << " location";
if(count != 2)
buf << "s";
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::saveState(int state)
{
mySystem.clearDirtyPages();
unlockBankswitchState();
myOSystem->state().saveState(state);
lockBankswitchState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::loadState(int state)
{
mySystem.clearDirtyPages();
unlockBankswitchState();
myOSystem->state().loadState(state);
lockBankswitchState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Debugger::step()
{
saveOldState();
mySystem.clearDirtyPages();
int cyc = mySystem.cycles();
unlockBankswitchState();
myOSystem->console().tia().updateScanlineByStep();
lockBankswitchState();
return mySystem.cycles() - cyc;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// trace is just like step, except it treats a subroutine call as one
// instruction.
// This implementation is not perfect: it just watches the program counter,
// instead of tracking (possibly) nested JSR/RTS pairs. In particular, it
// will fail for recursive subroutine calls. However, with 128 bytes of RAM
// to share between stack and variables, I doubt any 2600 games will ever
// use recursion...
int Debugger::trace()
{
// 32 is the 6502 JSR instruction:
if(mySystem.peek(myCpuDebug->pc()) == 32)
{
saveOldState();
mySystem.clearDirtyPages();
int cyc = mySystem.cycles();
int targetPC = myCpuDebug->pc() + 3; // return address
unlockBankswitchState();
myOSystem->console().tia().updateScanlineByTrace(targetPC);
lockBankswitchState();
return mySystem.cycles() - cyc;
}
else
return step();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleBreakPoint(int bp)
{
mySystem.m6502().setBreakPoints(myBreakPoints);
if(bp < 0) bp = myCpuDebug->pc();
myBreakPoints->toggle(bp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setBreakPoint(int bp, bool set)
{
mySystem.m6502().setBreakPoints(myBreakPoints);
if(bp < 0) bp = myCpuDebug->pc();
if(set)
myBreakPoints->set(bp);
else
myBreakPoints->clear(bp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::breakPoint(int bp)
{
if(bp < 0) bp = myCpuDebug->pc();
return myBreakPoints->isSet(bp) != 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleReadTrap(int t)
{
mySystem.m6502().setTraps(myReadTraps, myWriteTraps);
myReadTraps->toggle(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleWriteTrap(int t)
{
mySystem.m6502().setTraps(myReadTraps, myWriteTraps);
myWriteTraps->toggle(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleTrap(int t)
{
toggleReadTrap(t);
toggleWriteTrap(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::readTrap(int t)
{
return myReadTraps->isSet(t) != 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::writeTrap(int t)
{
return myWriteTraps->isSet(t) != 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Debugger::cycles()
{
return mySystem.cycles();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::nextScanline(int lines)
{
saveOldState();
mySystem.clearDirtyPages();
unlockBankswitchState();
while(lines)
{
myOSystem->console().tia().updateScanline();
--lines;
}
lockBankswitchState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::nextFrame(int frames)
{
saveOldState();
mySystem.clearDirtyPages();
unlockBankswitchState();
while(frames)
{
myOSystem->console().tia().update();
--frames;
}
lockBankswitchState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::rewindState()
{
mySystem.clearDirtyPages();
unlockBankswitchState();
bool result = myRewindManager->rewindState();
lockBankswitchState();
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::clearAllBreakPoints()
{
delete myBreakPoints;
myBreakPoints = new PackedBitArray(0x10000);
mySystem.m6502().setBreakPoints(NULL);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::clearAllTraps()
{
delete myReadTraps;
delete myWriteTraps;
myReadTraps = new PackedBitArray(0x10000);
myWriteTraps = new PackedBitArray(0x10000);
mySystem.m6502().setTraps(NULL, NULL);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Debugger::showWatches()
{
return myParser->showWatches();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::setBank(int bank)
{
if(myConsole.cartridge().bankCount() > 1)
{
myConsole.cartridge().unlockBank();
bool status = myConsole.cartridge().bank(bank);
myConsole.cartridge().lockBank();
return status;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::patchROM(int addr, int value)
{
return myConsole.cartridge().patch(addr, value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::saveOldState(bool addrewind)
{
myCartDebug->saveOldState();
myCpuDebug->saveOldState();
myRiotDebug->saveOldState();
myTiaDebug->saveOldState();
// Add another rewind level to the Undo list
if(addrewind) myRewindManager->addState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setStartState()
{
// Lock the bus each time the debugger is entered, so we don't disturb anything
lockBankswitchState();
// Start a new rewind list
myRewindManager->clear();
// Save initial state, but don't add it to the rewind list
saveOldState(false);
// Set the 're-disassemble' flag, but don't do it until the next scheduled time
myDialog->rom().invalidate(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setQuitState()
{
// Bus must be unlocked for normal operation when leaving debugger mode
unlockBankswitchState();
// execute one instruction on quit. If we're
// sitting at a breakpoint/trap, this will get us past it.
// Somehow this feels like a hack to me, but I don't know why
// if(myBreakPoints->isSet(myCpuDebug->pc()))
mySystem.m6502().execute(1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::addFunction(const string& name, const string& definition,
Expression* exp, bool builtin)
{
functions.insert(make_pair(name, exp));
if(!builtin)
functionDefs.insert(make_pair(name, definition));
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::delFunction(const string& name)
{
FunctionMap::iterator iter = functions.find(name);
if(iter == functions.end())
return false;
functions.erase(name);
delete iter->second;
FunctionDefMap::iterator def_iter = functionDefs.find(name);
if(def_iter == functionDefs.end())
return false;
functionDefs.erase(name);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Expression* Debugger::getFunction(const string& name) const
{
FunctionMap::const_iterator iter = functions.find(name);
if(iter == functions.end())
return 0;
else
return iter->second;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& Debugger::getFunctionDef(const string& name) const
{
FunctionDefMap::const_iterator iter = functionDefs.find(name);
if(iter == functionDefs.end())
return EmptyString;
else
return iter->second;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const FunctionDefMap Debugger::getFunctionDefMap() const
{
return functionDefs;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Debugger::builtinHelp() const
{
ostringstream buf;
uInt16 len, c_maxlen = 0, i_maxlen = 0;
// Get column widths for aligned output (functions)
for(int i = 0; builtin_functions[i][0] != 0; ++i)
{
len = strlen(builtin_functions[i][0]);
if(len > c_maxlen) c_maxlen = len;
len = strlen(builtin_functions[i][1]);
if(len > i_maxlen) i_maxlen = len;
}
buf << setfill(' ') << endl << "Built-in functions:" << endl;
for(int i = 0; builtin_functions[i][0] != 0; ++i)
{
buf << setw(c_maxlen) << left << builtin_functions[i][0]
<< setw(2) << right << "{"
<< setw(i_maxlen) << left << builtin_functions[i][1]
<< setw(4) << "}"
<< builtin_functions[i][2]
<< endl;
}
// Get column widths for aligned output (pseudo-registers)
c_maxlen = 0;
for(int i = 0; pseudo_registers[i][0] != 0; ++i)
{
len = strlen(pseudo_registers[i][0]);
if(len > c_maxlen) c_maxlen = len;
}
buf << endl << "Pseudo-registers:" << endl;
for(int i = 0; pseudo_registers[i][0] != 0; ++i)
{
buf << setw(c_maxlen) << left << pseudo_registers[i][0]
<< setw(2) << " "
<< setw(i_maxlen) << left << pseudo_registers[i][1]
<< endl;
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::getCompletions(const char* in, StringList& list) const
{
FunctionMap::const_iterator iter;
for(iter = functions.begin(); iter != functions.end(); ++iter)
{
const char* l = iter->first.c_str();
if(BSPF_equalsIgnoreCase(l, in))
list.push_back(l);
}
for(int i = 0; pseudo_registers[i][0] != 0; ++i)
if(BSPF_equalsIgnoreCase(pseudo_registers[i][0], in))
list.push_back(pseudo_registers[i][0]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::lockBankswitchState()
{
mySystem.lockDataBus();
myConsole.cartridge().lockBank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::unlockBankswitchState()
{
mySystem.unlockDataBus();
myConsole.cartridge().unlockBank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::RewindManager::RewindManager(OSystem& system, ButtonWidget& button)
: myOSystem(system),
myRewindButton(button),
mySize(0),
myTop(0)
{
for(int i = 0; i < MAX_SIZE; ++i)
myStateList[i] = (Serializer*) NULL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::RewindManager::~RewindManager()
{
for(int i = 0; i < MAX_SIZE; ++i)
delete myStateList[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::RewindManager::addState()
{
// Create a new Serializer object if we need one
if(myStateList[myTop] == NULL)
myStateList[myTop] = new Serializer();
Serializer& s = *(myStateList[myTop]);
if(s.isValid())
{
s.reset();
if(myOSystem.state().saveState(s) && myOSystem.console().tia().saveDisplay(s))
{
// Are we still within the allowable size, or are we overwriting an item?
mySize++; if(mySize > MAX_SIZE) mySize = MAX_SIZE;
myTop = (myTop + 1) % MAX_SIZE;
myRewindButton.setEnabled(true);
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::RewindManager::rewindState()
{
if(mySize > 0)
{
mySize--;
myTop = myTop == 0 ? MAX_SIZE - 1 : myTop - 1;
Serializer& s = *(myStateList[myTop]);
s.reset();
myOSystem.state().loadState(s);
myOSystem.console().tia().loadDisplay(s);
if(mySize == 0)
myRewindButton.setEnabled(false);
return true;
}
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::RewindManager::isEmpty()
{
return mySize == 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::RewindManager::clear()
{
for(int i = 0; i < MAX_SIZE; ++i)
if(myStateList[i] != NULL)
myStateList[i]->reset();
myTop = mySize = 0;
// We use Widget::clearFlags here instead of Widget::setEnabled(),
// since the latter implies an immediate draw/update, but this method
// might be called before any UI exists
// TODO - fix this deficiency in the UI core; we shouldn't have to worry
// about such things at this level
myRewindButton.clearFlags(WIDGET_ENABLED);
}
+344
View File
@@ -0,0 +1,344 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Debugger.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef DEBUGGER_HXX
#define DEBUGGER_HXX
class OSystem;
class Console;
class CartDebug;
class CpuDebug;
class RiotDebug;
class TIADebug;
class M6502;
class TiaInfoWidget;
class TiaOutputWidget;
class TiaZoomWidget;
class EditTextWidget;
class RomWidget;
class Expression;
class Serializer;
class PackedBitArray;
class PromptWidget;
class ButtonWidget;
#include <map>
#include "Array.hxx"
#include "Base.hxx"
#include "DialogContainer.hxx"
#include "DebuggerDialog.hxx"
#include "DebuggerParser.hxx"
#include "System.hxx"
#include "Stack.hxx"
#include "bspf.hxx"
typedef map<string,Expression*> FunctionMap;
typedef map<string,string> FunctionDefMap;
/*
// These will probably turn out to be unneeded, left for reference for now
// pointer types for Debugger instance methods
typedef uInt8 (Debugger::*DEBUGGER_BYTE_METHOD)();
typedef uInt16 (Debugger::*DEBUGGER_WORD_METHOD)();
// call the pointed-to method on the (static) debugger object.
#define CALL_DEBUGGER_METHOD(method) ( ( Debugger::debugger().*method)() )
*/
/**
The base dialog for all debugging widgets in Stella. Also acts as the parent
for all debugging operations in Stella (parser, 6502 debugger, etc).
@author Stephen Anthony
@version $Id: Debugger.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class Debugger : public DialogContainer
{
// Make these friend classes, to ease communications with the debugger
// Although it isn't enforced, these classes should use accessor methods
// directly, and not touch the instance variables
friend class DebuggerParser;
friend class EventHandler;
public:
/**
Create a new debugger parent object
*/
Debugger(OSystem& osystem, Console& console);
/**
Destructor
*/
virtual ~Debugger();
public:
/**
Initialize the debugger dialog container.
*/
void initialize();
/**
Initialize the video subsystem wrt this class.
*/
FBInitStatus initializeVideo();
/**
Wrapper method for EventHandler::enterDebugMode() for those classes
that don't have access to EventHandler.
@param message Message to display when entering debugger
@param data An address associated with the message
*/
bool start(const string& message = "", int address = -1);
bool startWithFatalError(const string& message = "");
/**
Wrapper method for EventHandler::leaveDebugMode() for those classes
that don't have access to EventHandler.
*/
void quit(bool exitrom);
bool addFunction(const string& name, const string& def,
Expression* exp, bool builtin = false);
bool delFunction(const string& name);
const Expression* getFunction(const string& name) const;
const string& getFunctionDef(const string& name) const;
const FunctionDefMap getFunctionDefMap() const;
string builtinHelp() const;
/**
Methods used by the command parser for tab-completion
In this case, return completions from the function list
*/
void getCompletions(const char* in, StringList& list) const;
/**
The dialog/GUI associated with the debugger
*/
Dialog& dialog() const { return *myDialog; }
/**
The debugger subsystem responsible for all CPU state
*/
CpuDebug& cpuDebug() const { return *myCpuDebug; }
/**
The debugger subsystem responsible for all Cart RAM/ROM state
*/
CartDebug& cartDebug() const { return *myCartDebug; }
/**
The debugger subsystem responsible for all RIOT state
*/
RiotDebug& riotDebug() const { return *myRiotDebug; }
/**
The debugger subsystem responsible for all TIA state
*/
TIADebug& tiaDebug() const { return *myTiaDebug; }
const GUI::Font& lfont() const { return myDialog->lfont(); }
const GUI::Font& nlfont() const { return myDialog->nfont(); }
DebuggerParser& parser() const { return *myParser; }
PackedBitArray& breakpoints() const { return *myBreakPoints; }
PackedBitArray& readtraps() const { return *myReadTraps; }
PackedBitArray& writetraps() const { return *myWriteTraps; }
PromptWidget& prompt() const { return myDialog->prompt(); }
RomWidget& rom() const { return myDialog->rom(); }
/**
Run the debugger command and return the result.
*/
const string run(const string& command);
/**
The current cycle count of the System.
*/
int cycles();
string autoExec();
string showWatches();
/**
Convert between string->integer and integer->string, taking into
account the current base format.
*/
int stringToValue(const string& stringval)
{ return myParser->decipher_arg(stringval); }
/* Convenience methods to get/set bit(s) in an 8-bit register */
static uInt8 set_bit(uInt8 input, uInt8 bit, bool on)
{
if(on)
return input | (1 << bit);
else
return input & ~(1 << bit);
}
static void set_bits(uInt8 reg, BoolArray& bits)
{
bits.clear();
for(int i = 0; i < 8; ++i)
{
if(reg & (1<<(7-i)))
bits.push_back(true);
else
bits.push_back(false);
}
}
static uInt8 get_bits(BoolArray& bits)
{
uInt8 result = 0x0;
for(int i = 0; i < 8; ++i)
if(bits[i])
result |= (1<<(7-i));
return result;
}
/* Invert given input if it differs from its previous value */
const string invIfChanged(int reg, int oldReg);
/**
This is used when we want the debugger from a class that can't
receive the debugger object in any other way.
It's basically a hack to prevent the need to pass debugger objects
everywhere, but I feel it's better to place it here then in
YaccParser (which technically isn't related to it at all).
*/
static Debugger& debugger() { return *myStaticDebugger; }
/* These are now exposed so Expressions can use them. */
int peek(int addr) { return mySystem.peek(addr); }
int dpeek(int addr) { return mySystem.peek(addr) | (mySystem.peek(addr+1) << 8); }
int getAccessFlags(uInt16 addr)
{ return mySystem.getAccessFlags(addr); }
void setAccessFlags(uInt16 addr, uInt8 flags)
{ mySystem.setAccessFlags(addr, flags); }
void setBreakPoint(int bp, bool set);
bool setBank(int bank);
bool patchROM(int addr, int value);
/**
Normally, accessing RAM or ROM during emulation can possibly trigger
bankswitching. However, when we're in the debugger, we'd like to
inspect values without actually triggering bankswitches. The
read/write state must therefore be locked before accessing values,
and unlocked for normal emulation to occur.
(takes mediasource into account)
*/
void lockBankswitchState();
void unlockBankswitchState();
private:
/**
Save state of each debugger subsystem.
*/
void saveOldState(bool addrewind = true);
/**
Set initial state before entering the debugger.
*/
void setStartState();
/**
Set final state before leaving the debugger.
*/
void setQuitState();
int step();
int trace();
void nextScanline(int lines);
void nextFrame(int frames);
bool rewindState();
void toggleBreakPoint(int bp);
bool breakPoint(int bp);
void toggleReadTrap(int t);
void toggleWriteTrap(int t);
void toggleTrap(int t);
bool readTrap(int t);
bool writeTrap(int t);
void clearAllTraps();
// Set a bunch of RAM locations at once
string setRAM(IntArray& args);
void reset();
void clearAllBreakPoints();
void saveState(int state);
void loadState(int state);
private:
Console& myConsole;
System& mySystem;
DebuggerDialog* myDialog;
DebuggerParser* myParser;
CartDebug* myCartDebug;
CpuDebug* myCpuDebug;
RiotDebug* myRiotDebug;
TIADebug* myTiaDebug;
PackedBitArray* myBreakPoints;
PackedBitArray* myReadTraps;
PackedBitArray* myWriteTraps;
static Debugger* myStaticDebugger;
FunctionMap functions;
FunctionDefMap functionDefs;
// Dimensions of the entire debugger window
uInt32 myWidth;
uInt32 myHeight;
// Class holding all rewind state functionality in the debugger
// Essentially, it's a modified circular array-based stack
// that cleverly deals with allocation/deallocation of memory
class RewindManager
{
public:
RewindManager(OSystem& system, ButtonWidget& button);
virtual ~RewindManager();
public:
bool addState();
bool rewindState();
bool isEmpty();
void clear();
private:
enum { MAX_SIZE = 100 };
OSystem& myOSystem;
ButtonWidget& myRewindButton;
Serializer* myStateList[MAX_SIZE];
uInt32 mySize, myTop;
};
RewindManager* myRewindManager;
};
#endif
+347
View File
@@ -0,0 +1,347 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: DebuggerExpressions.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef DEBUGGER_EXPRESSIONS_HXX
#define DEBUGGER_EXPRESSIONS_HXX
#include "bspf.hxx"
#include "CartDebug.hxx"
#include "CpuDebug.hxx"
#include "TIADebug.hxx"
#include "Debugger.hxx"
#include "Expression.hxx"
/**
All expressions currently supported by the debugger.
@author B. Watson and Stephen Anthony
*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class BinAndExpression : public Expression
{
public:
BinAndExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() & myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class BinNotExpression : public Expression
{
public:
BinNotExpression(Expression* left) : Expression(left, 0) {}
uInt16 evaluate() const
{ return ~(myLHS->evaluate()); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class BinOrExpression : public Expression
{
public:
BinOrExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() | myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class BinXorExpression : public Expression
{
public:
BinXorExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() ^ myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ByteDerefExpression : public Expression
{
public:
ByteDerefExpression(Expression* left): Expression(left, 0) {}
uInt16 evaluate() const
{ return Debugger::debugger().peek(myLHS->evaluate()); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ByteDerefOffsetExpression : public Expression
{
public:
ByteDerefOffsetExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return Debugger::debugger().peek(myLHS->evaluate() + myRHS->evaluate()); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ConstExpression : public Expression
{
public:
ConstExpression(const int value) : Expression(0, 0), myValue(value) {}
uInt16 evaluate() const
{ return myValue; }
private:
int myValue;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class CpuMethodExpression : public Expression
{
public:
CpuMethodExpression(CPUDEBUG_INT_METHOD method) : Expression(0, 0), myMethod(method) {}
uInt16 evaluate() const
{ return CALL_CPUDEBUG_METHOD(myMethod); }
private:
CPUDEBUG_INT_METHOD myMethod;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class DivExpression : public Expression
{
public:
DivExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ int denom = myRHS->evaluate();
return denom == 0 ? 0 : myLHS->evaluate() / denom; }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class EqualsExpression : public Expression
{
public:
EqualsExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() == myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class EquateExpression : public Expression
{
public:
EquateExpression(const string& label) : Expression(0, 0), myLabel(label) {}
uInt16 evaluate() const
{ return Debugger::debugger().cartDebug().getAddress(myLabel); }
private:
string myLabel;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class FunctionExpression : public Expression
{
public:
FunctionExpression(const string& label) : Expression(0, 0), myLabel(label) {}
uInt16 evaluate() const
{
const Expression* exp = Debugger::debugger().getFunction(myLabel);
if(exp) return exp->evaluate();
else return 0;
}
private:
string myLabel;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class GreaterEqualsExpression : public Expression
{
public:
GreaterEqualsExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() >= myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class GreaterExpression : public Expression
{
public:
GreaterExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() > myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class HiByteExpression : public Expression
{
public:
HiByteExpression(Expression* left) : Expression(left, 0) {}
uInt16 evaluate() const
{ return 0xff & (myLHS->evaluate() >> 8); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class LessEqualsExpression : public Expression
{
public:
LessEqualsExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() <= myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class LessExpression : public Expression
{
public:
LessExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() < myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class LoByteExpression : public Expression
{
public:
LoByteExpression(Expression* left) : Expression(left, 0) {}
uInt16 evaluate() const
{ return 0xff & myLHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class LogAndExpression : public Expression
{
public:
LogAndExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() && myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class LogNotExpression : public Expression
{
public:
LogNotExpression(Expression* left) : Expression(left, 0) {}
uInt16 evaluate() const
{ return !(myLHS->evaluate()); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class LogOrExpression : public Expression
{
public:
LogOrExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() || myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class MinusExpression : public Expression
{
public:
MinusExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() - myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ModExpression : public Expression
{
public:
ModExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ int rhs = myRHS->evaluate();
return rhs == 0 ? 0 : myLHS->evaluate() % rhs; }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class MultExpression : public Expression
{
public:
MultExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() * myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class NotEqualsExpression : public Expression
{
public:
NotEqualsExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() != myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class PlusExpression : public Expression
{
public:
PlusExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() + myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class CartMethodExpression : public Expression
{
public:
CartMethodExpression(CARTDEBUG_INT_METHOD method) : Expression(0, 0), myMethod(method) {}
uInt16 evaluate() const
{ return CALL_CARTDEBUG_METHOD(myMethod); }
private:
CARTDEBUG_INT_METHOD myMethod;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ShiftLeftExpression : public Expression
{
public:
ShiftLeftExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() << myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ShiftRightExpression : public Expression
{
public:
ShiftRightExpression(Expression* left, Expression* right) : Expression(left, right) {}
uInt16 evaluate() const
{ return myLHS->evaluate() >> myRHS->evaluate(); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class TiaMethodExpression : public Expression
{
public:
TiaMethodExpression(TIADEBUG_INT_METHOD method) : Expression(0, 0), myMethod(method) {}
uInt16 evaluate() const
{ return CALL_TIADEBUG_METHOD(myMethod); }
private:
TIADEBUG_INT_METHOD myMethod;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class UnaryMinusExpression : public Expression
{
public:
UnaryMinusExpression(Expression* left) : Expression(left, 0) {}
uInt16 evaluate() const
{ return -(myLHS->evaluate()); }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class WordDerefExpression : public Expression
{
public:
WordDerefExpression(Expression* left) : Expression(left, 0) {}
uInt16 evaluate() const
{ return Debugger::debugger().dpeek(myLHS->evaluate()); }
};
#endif
File diff suppressed because it is too large Load Diff
+203
View File
@@ -0,0 +1,203 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: DebuggerParser.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef DEBUGGER_PARSER_HXX
#define DEBUGGER_PARSER_HXX
#include <sstream>
class Debugger;
class FilesystemNode;
struct Command;
#include "bspf.hxx"
#include "Array.hxx"
#include "FrameBuffer.hxx"
#include "Settings.hxx"
class DebuggerParser
{
public:
DebuggerParser(Debugger& debugger, Settings& settings);
~DebuggerParser();
/** Run the given command, and return the result */
string run(const string& command);
/** Execute parser commands given in 'file' */
string exec(const FilesystemNode& file);
/** Given a substring, determine matching substrings from the list
of available commands. Used in the debugger prompt for tab-completion */
void getCompletions(const char* in, StringList& list) const;
/** Evaluate the given expression using operators, current base, etc */
int decipher_arg(const string &str);
/** String representation of all watches currently defined */
string showWatches();
static inline string red(const string& msg = "")
{
return char(kDbgChangedColor) + msg;
}
static inline string inverse(const string& msg = "")
{
// ASCII DEL char, decimal 127
return "\177" + msg;
}
private:
bool getArgs(const string& command, string& verb);
bool validateArgs(int cmd);
string eval();
string trapStatus(int addr);
bool saveScriptFile(string file);
private:
enum {
kNumCommands = 70,
kMAX_ARG_TYPES = 10
};
// Constants for argument processing
enum {
kIN_COMMAND,
kIN_SPACE,
kIN_BRACE,
kIN_ARG
};
typedef enum {
kARG_WORD, // single 16-bit value
kARG_MULTI_WORD, // multiple 16-bit values (must occur last)
kARG_BYTE, // single 8-bit value
kARG_MULTI_BYTE, // multiple 8-bit values (must occur last)
kARG_BOOL, // 0 or 1 only
kARG_LABEL, // label (need not be defined, treated as string)
kARG_FILE, // filename
kARG_BASE_SPCL, // base specifier: 2, 10, or 16 (or "bin" "dec" "hex")
kARG_END_ARGS // sentinel, occurs at end of list
} parameters;
// Pointer to DebuggerParser instance method, no args, returns void.
typedef void (DebuggerParser::*METHOD)();
struct Command {
string cmdString;
string description;
bool parmsRequired;
bool refreshRequired;
parameters parms[kMAX_ARG_TYPES];
METHOD executor;
};
// Reference to our debugger object
Debugger& debugger;
// Reference to settings object (required for saving certain options)
Settings& settings;
// The results of the currently running command
ostringstream commandResult;
// Arguments in 'int' and 'string' format for the currently running command
IntArray args;
StringList argStrings;
int argCount;
StringList watches;
// List of available command methods
void executeA();
void executeBank();
void executeBase();
void executeBreak();
void executeBreakif();
void executeC();
void executeCheat();
void executeClearbreaks();
void executeClearconfig();
void executeCleartraps();
void executeClearwatches();
void executeCls();
void executeCode();
void executeColortest();
void executeD();
void executeData();
void executeDefine();
void executeDelbreakif();
void executeDelfunction();
void executeDelwatch();
void executeDisasm();
void executeDump();
void executeExec();
void executeExitRom();
void executeFrame();
void executeFunction();
void executeGfx();
void executeHelp();
void executeJump();
void executeListbreaks();
void executeListconfig();
void executeListfunctions();
void executeListtraps();
void executeLoadconfig();
void executeLoadstate();
void executeN();
void executePc();
void executePGfx();
void executePrint();
void executeRam();
void executeReset();
void executeRewind();
void executeRiot();
void executeRom();
void executeRow();
void executeRun();
void executeRunTo();
void executeRunToPc();
void executeS();
void executeSave();
void executeSaveconfig();
void executeSavedisassembly();
void executeSaverom();
void executeSaveses();
void executeSavestate();
void executeScanline();
void executeStep();
void executeTia();
void executeTrace();
void executeTrap();
void executeTrapread();
void executeTrapwrite();
void executeType();
void executeUHex();
void executeUndef();
void executeV();
void executeWatch();
void executeX();
void executeY();
void executeZ();
// List of commands available
static Command commands[kNumCommands];
};
#endif
+63
View File
@@ -0,0 +1,63 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: DebuggerSystem.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef DEBUGGER_SYSTEM_HXX
#define DEBUGGER_SYSTEM_HXX
class Debugger;
#include "Console.hxx"
/**
The DebuggerState class is used as a base class for state in all
DebuggerSystem objects. We make it a class so we can take advantage
of the copy constructor.
*/
class DebuggerState
{
public:
DebuggerState() { }
~DebuggerState() { }
};
/**
The base class for all debugger objects. Its real purpose is to
clean up the Debugger API, partitioning it into separate
subsystems.
*/
class DebuggerSystem
{
public:
DebuggerSystem(Debugger& dbg, Console& console) :
myDebugger(dbg), myConsole(console), mySystem(console.system()) { }
virtual ~DebuggerSystem() { }
virtual const DebuggerState& getState() = 0;
virtual const DebuggerState& getOldState() = 0;
virtual void saveOldState() = 0;
virtual string toString() = 0;
protected:
Debugger& myDebugger;
Console& myConsole;
System& mySystem;
};
#endif
File diff suppressed because it is too large Load Diff
+191
View File
@@ -0,0 +1,191 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: DiStella.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef DISTELLA_HXX
#define DISTELLA_HXX
#include <queue>
#include <sstream>
#include "Array.hxx"
#include "Base.hxx"
#include "CartDebug.hxx"
#include "bspf.hxx"
/**
This class is a wrapper around the Distella code. Much of the code remains
exactly the same, except that generated data is now redirected to a
DisassemblyList structure rather than being printed.
All 7800-related stuff has been removed, as well as all commandline options.
Over time, some of the configurability of Distella may be added again.
@author Stephen Anthony
*/
class DiStella
{
public:
// A list of options that can be applied to the disassembly
// This will eventually grow to include all options supported by
// standalone Distella
typedef struct {
Common::Base::Format gfx_format;
bool resolve_code; // Attempt to detect code vs. data sections
bool show_addresses; // Show PC addresses (always off for external output)
bool aflag; // Turns 'A' off in accumulator instructions (-a in Distella)
bool fflag; // Forces correct address length (-f in Distella)
bool rflag; // Relocate calls out of address range (-r in Distella)
int bwidth; // Number of bytes to use per line (with .byte xxx)
} Settings;
static Settings settings; // Default settings
public:
/**
Disassemble the current state of the System from the given start address.
@param dbg The CartDebug instance containing all label information
@param list The results of the disassembly are placed here
@param info Various info about the current bank
@param settings The various distella flags/options to use
@param labels Array storing label info determined by Distella
@param directives Array storing directive info determined by Distella
@param reserved The TIA/RIOT addresses referenced in the disassembled code
*/
DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list,
CartDebug::BankInfo& info, const DiStella::Settings& settings,
uInt8* labels, uInt8* directives,
CartDebug::ReservedEquates& reserved);
~DiStella();
private:
// Indicate that a new line of disassembly has been completed
// In the original Distella code, this indicated a new line to be printed
// Here, we add a new entry to the DisassemblyList
void addEntry(CartDebug::DisasmType type);
// Process directives given in the list
// Directives are basically the contents of a distella configuration file
void processDirectives(const CartDebug::DirectiveList& directives);
// These functions are part of the original Distella code
void disasm(uInt32 distart, int pass);
bool check_range(uInt16 start, uInt16 end) const;
int mark(uInt32 address, uInt8 mask, bool directive = false);
bool check_bit(uInt16 address, uInt8 mask) const;
// Convenience methods to generate appropriate labels
inline void labelA12High(stringstream& buf, uInt8 op, uInt16 addr, int labfound)
{
if(!myDbg.getLabel(buf, addr, true))
buf << "L" << Common::Base::HEX4 << addr;
}
inline void labelA12Low(stringstream& buf, uInt8 op, uInt16 addr, int labfound)
{
myDbg.getLabel(buf, addr, ourLookup[op].rw_mode == READ, 2);
if (labfound == 2)
{
if(ourLookup[op].rw_mode == READ)
myReserved.TIARead[addr & 0x0F] = true;
else
myReserved.TIAWrite[addr & 0x3F] = true;
}
else if (labfound == 3)
myReserved.IOReadWrite[(addr & 0xFF) - 0x80] = true;
else if (labfound == 5)
myReserved.ZPRAM[(addr & 0xFF) - 0x80] = true;
}
private:
const CartDebug& myDbg;
CartDebug::DisassemblyList& myList;
const Settings& mySettings;
CartDebug::ReservedEquates& myReserved;
stringstream myDisasmBuf;
queue<uInt16> myAddressQueue;
uInt16 myOffset, myPC, myPCBeg, myPCEnd;
struct resource {
uInt16 start;
uInt16 end;
uInt16 length;
} myAppData;
/* Stores info on how each address is marked, both in the general
case as well as when manual directives are enabled (in which case
the directives take priority
The address mark type is defined in CartDebug.hxx
*/
uInt8 *myLabels, *myDirectives;
/**
Enumeration of the 6502 addressing modes
*/
enum AddressingMode
{
IMPLIED, ACCUMULATOR, IMMEDIATE,
ZERO_PAGE, ZERO_PAGE_X, ZERO_PAGE_Y,
ABSOLUTE, ABSOLUTE_X, ABSOLUTE_Y,
ABS_INDIRECT, INDIRECT_X, INDIRECT_Y,
RELATIVE, ASS_CODE
};
/**
Enumeration of the 6502 access modes
*/
enum AccessMode
{
M_NONE, M_AC, M_XR, M_YR, M_SP, M_SR, M_PC, M_IMM, M_ZERO, M_ZERX, M_ZERY,
M_ABS, M_ABSX, M_ABSY, M_AIND, M_INDX, M_INDY, M_REL, M_FC, M_FD, M_FI,
M_FV, M_ADDR, M_,
M_ACIM, /* Source: AC & IMMED (bus collision) */
M_ANXR, /* Source: AC & XR (bus collision) */
M_AXIM, /* Source: (AC | #EE) & XR & IMMED (bus collision) */
M_ACNC, /* Dest: M_AC and Carry = Negative */
M_ACXR, /* Dest: M_AC, M_XR */
M_SABY, /* Source: (ABS_Y & SP) (bus collision) */
M_ACXS, /* Dest: M_AC, M_XR, M_SP */
M_STH0, /* Dest: Store (src & Addr_Hi+1) to (Addr +0x100) */
M_STH1,
M_STH2,
M_STH3
};
/**
Enumeration of the 6502 read/write mode
(if the opcode is reading or writing its operand)
*/
enum ReadWriteMode
{
READ, WRITE, NONE
};
struct Instruction_tag {
const char* mnemonic;
AddressingMode addr_mode;
AccessMode source;
ReadWriteMode rw_mode;
uInt8 cycles;
};
static const Instruction_tag ourLookup[256];
};
#endif
+47
View File
@@ -0,0 +1,47 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Expression.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Expression.hxx"
#ifdef EXPR_REF_COUNT
#include "bspf.hxx"
int refCount = 0;
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expression::Expression(Expression* lhs, Expression* rhs)
: myLHS(lhs),
myRHS(rhs)
{
#ifdef EXPR_REF_COUNT
refCount++;
cerr << "new Expression::Expression()" << endl;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expression::~Expression()
{
#ifdef EXPR_REF_COUNT
refCount--;
cerr << "~Expression::Expression()" << endl;
#endif
delete myLHS;
delete myRHS;
}
+51
View File
@@ -0,0 +1,51 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Expression.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef EXPRESSION_HXX
#define EXPRESSION_HXX
#include "bspf.hxx"
// define this to count Expression instances. Only useful for debugging
// Stella itself.
//#define EXPR_REF_COUNT
/**
This class provides an implementation of an expression node, which
is a construct that is given two other expressions and evaluates and
returns the result. When placed in a tree, a collection of such nodes
can represent complex expression statements.
@author Stephen Anthony
@version $Id: Expression.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
class Expression
{
public:
Expression(Expression* lhs, Expression* rhs);
virtual ~Expression();
virtual uInt16 evaluate() const = 0;
protected:
Expression* myLHS;
Expression* myRHS;
};
#endif
+82
View File
@@ -0,0 +1,82 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: PackedBitArray.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "bspf.hxx"
#include "PackedBitArray.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackedBitArray::PackedBitArray(uInt32 length)
: words(length / wordSize + 1)
{
bits = new uInt32[ words ];
for(uInt32 i = 0; i < words; ++i)
bits[i] = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackedBitArray::~PackedBitArray()
{
delete[] bits;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 PackedBitArray::isSet(uInt32 bit) const
{
uInt32 word = bit / wordSize;
bit %= wordSize;
return (bits[word] & (1 << bit));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 PackedBitArray::isClear(uInt32 bit) const
{
uInt32 word = bit / wordSize;
bit %= wordSize;
return !(bits[word] & (1 << bit));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PackedBitArray::toggle(uInt32 bit)
{
uInt32 word = bit / wordSize;
bit %= wordSize;
bits[word] ^= (1 << bit);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PackedBitArray::set(uInt32 bit)
{
uInt32 word = bit / wordSize;
bit %= wordSize;
bits[word] |= (1 << bit);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PackedBitArray::clear(uInt32 bit)
{
uInt32 word = bit / wordSize;
bit %= wordSize;
bits[word] &= (~(1 << bit));
}
+48
View File
@@ -0,0 +1,48 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: PackedBitArray.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef PACKEDBITARRAY_HXX
#define PACKEDBITARRAY_HXX
#include "bspf.hxx"
#define wordSize ( (sizeof(unsigned int)) * 8)
class PackedBitArray
{
public:
PackedBitArray(uInt32 length);
~PackedBitArray();
uInt32 isSet(uInt32 bit) const;
uInt32 isClear(uInt32 bit) const;
void set(uInt32 bit);
void clear(uInt32 bit);
void toggle(uInt32 bit);
private:
// number of unsigned ints (size/wordSize):
uInt32 words;
// the array itself:
uInt32* bits;
};
#endif
+354
View File
@@ -0,0 +1,354 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RiotDebug.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include <sstream>
#include "System.hxx"
#include "TIA.hxx"
#include "Debugger.hxx"
#include "Switches.hxx"
#include "RiotDebug.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RiotDebug::RiotDebug(Debugger& dbg, Console& console)
: DebuggerSystem(dbg, console)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const DebuggerState& RiotDebug::getState()
{
// Port A & B registers
myState.SWCHA_R = swcha();
myState.SWCHA_W = mySystem.m6532().myOutA;
myState.SWACNT = swacnt();
myState.SWCHB_R = swchb();
myState.SWCHB_W = mySystem.m6532().myOutB;
myState.SWBCNT = swbcnt();
Debugger::set_bits(myState.SWCHA_R, myState.swchaReadBits);
Debugger::set_bits(myState.SWCHA_W, myState.swchaWriteBits);
Debugger::set_bits(myState.SWACNT, myState.swacntBits);
Debugger::set_bits(myState.SWCHB_R, myState.swchbReadBits);
Debugger::set_bits(myState.SWCHB_W, myState.swchbWriteBits);
Debugger::set_bits(myState.SWBCNT, myState.swbcntBits);
// TIA INPTx registers
myState.INPT0 = inpt(0);
myState.INPT1 = inpt(1);
myState.INPT2 = inpt(2);
myState.INPT3 = inpt(3);
myState.INPT4 = inpt(4);
myState.INPT5 = inpt(5);
// Timer registers
myState.TIM1T = tim1T();
myState.TIM8T = tim8T();
myState.TIM64T = tim64T();
myState.T1024T = tim1024T();
myState.INTIM = intim();
myState.TIMINT = timint();
myState.TIMCLKS = timClocks();
myState.INTIMCLKS = intimClocks();
return myState;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RiotDebug::saveOldState()
{
// Port A & B registers
myOldState.SWCHA_R = swcha();
myOldState.SWCHA_W = mySystem.m6532().myOutA;
myOldState.SWACNT = swacnt();
myOldState.SWCHB_R = swchb();
myOldState.SWCHB_W = mySystem.m6532().myOutB;
myOldState.SWBCNT = swbcnt();
Debugger::set_bits(myOldState.SWCHA_R, myOldState.swchaReadBits);
Debugger::set_bits(myOldState.SWCHA_W, myOldState.swchaWriteBits);
Debugger::set_bits(myOldState.SWACNT, myOldState.swacntBits);
Debugger::set_bits(myOldState.SWCHB_R, myOldState.swchbReadBits);
Debugger::set_bits(myOldState.SWCHB_W, myOldState.swchbWriteBits);
Debugger::set_bits(myOldState.SWBCNT, myOldState.swbcntBits);
// TIA INPTx registers
myOldState.INPT0 = inpt(0);
myOldState.INPT1 = inpt(1);
myOldState.INPT2 = inpt(2);
myOldState.INPT3 = inpt(3);
myOldState.INPT4 = inpt(4);
myOldState.INPT5 = inpt(5);
// Timer registers
myOldState.TIM1T = tim1T();
myOldState.TIM8T = tim8T();
myOldState.TIM64T = tim64T();
myOldState.T1024T = tim1024T();
myOldState.INTIM = intim();
myOldState.TIMINT = timint();
myOldState.TIMCLKS = timClocks();
myOldState.INTIMCLKS = intimClocks();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::swcha(int newVal)
{
if(newVal > -1)
mySystem.poke(0x280, newVal);
return mySystem.peek(0x280);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::swchb(int newVal)
{
if(newVal > -1)
mySystem.poke(0x282, newVal);
return mySystem.peek(0x282);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::swacnt(int newVal)
{
if(newVal > -1)
mySystem.poke(0x281, newVal);
return mySystem.m6532().myDDRA;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::swbcnt(int newVal)
{
if(newVal > -1)
mySystem.poke(0x283, newVal);
return mySystem.m6532().myDDRB;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::inpt(int x)
{
static TIARegister _inpt[6] = { INPT0, INPT1, INPT2, INPT3, INPT4, INPT5 };
return mySystem.peek(_inpt[x]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::vblank(int bit)
{
if(bit == 6) // latches
return mySystem.tia().myVBLANK & 0x40;
else if(bit == 7) // dump to ground
return mySystem.tia().myDumpEnabled;
else
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::tim1T(int newVal)
{
if(newVal > -1)
mySystem.poke(0x294, newVal);
return mySystem.m6532().myOutTimer[0];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::tim8T(int newVal)
{
if(newVal > -1)
mySystem.poke(0x295, newVal);
return mySystem.m6532().myOutTimer[1];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::tim64T(int newVal)
{
if(newVal > -1)
mySystem.poke(0x296, newVal);
return mySystem.m6532().myOutTimer[2];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 RiotDebug::tim1024T(int newVal)
{
if(newVal > -1)
mySystem.poke(0x297, newVal);
return mySystem.m6532().myOutTimer[3];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Controller& RiotDebug::controller(Controller::Jack jack) const
{
return myConsole.controller(jack);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::diffP0(int newVal)
{
uInt8& switches = myConsole.switches().mySwitches;
if(newVal > -1)
switches = Debugger::set_bit(switches, 6, newVal > 0);
return switches & 0x40;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::diffP1(int newVal)
{
uInt8& switches = myConsole.switches().mySwitches;
if(newVal > -1)
switches = Debugger::set_bit(switches, 7, newVal > 0);
return switches & 0x80;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::tvType(int newVal)
{
uInt8& switches = myConsole.switches().mySwitches;
if(newVal > -1)
switches = Debugger::set_bit(switches, 3, newVal > 0);
return switches & 0x08;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::select(int newVal)
{
uInt8& switches = myConsole.switches().mySwitches;
if(newVal > -1)
switches = Debugger::set_bit(switches, 1, newVal > 0);
return switches & 0x02;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::reset(int newVal)
{
uInt8& switches = myConsole.switches().mySwitches;
if(newVal > -1)
switches = Debugger::set_bit(switches, 0, newVal > 0);
return switches & 0x01;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::dirP0String()
{
uInt8 reg = swcha();
ostringstream buf;
buf << (reg & 0x80 ? "" : "right ")
<< (reg & 0x40 ? "" : "left ")
<< (reg & 0x20 ? "" : "left ")
<< (reg & 0x10 ? "" : "left ")
<< ((reg & 0xf0) == 0xf0 ? "(no directions) " : "");
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::dirP1String()
{
uInt8 reg = swcha();
ostringstream buf;
buf << (reg & 0x08 ? "" : "right ")
<< (reg & 0x04 ? "" : "left ")
<< (reg & 0x02 ? "" : "left ")
<< (reg & 0x01 ? "" : "left ")
<< ((reg & 0x0f) == 0x0f ? "(no directions) " : "");
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::diffP0String()
{
return (swchb() & 0x40) ? "hard/A" : "easy/B";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::diffP1String()
{
return (swchb() & 0x80) ? "hard/A" : "easy/B";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::tvTypeString()
{
return (swchb() & 0x8) ? "Color" : "B&W";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::switchesString()
{
ostringstream buf;
buf << (swchb() & 0x2 ? "-" : "+") << "select "
<< (swchb() & 0x1 ? "-" : "+") << "reset";
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RiotDebug::toString()
{
const RiotState& state = (RiotState&) getState();
const RiotState& oldstate = (RiotState&) getOldState();
ostringstream buf;
buf << "280/SWCHA(R)=" << myDebugger.invIfChanged(state.SWCHA_R, oldstate.SWCHA_R)
<< " 280/SWCHA(W)=" << myDebugger.invIfChanged(state.SWCHA_W, oldstate.SWCHA_W)
<< " 281/SWACNT=" << myDebugger.invIfChanged(state.SWACNT, oldstate.SWACNT)
<< endl
<< "282/SWCHB(R)=" << myDebugger.invIfChanged(state.SWCHB_R, oldstate.SWCHB_R)
<< " 282/SWCHB(W)=" << myDebugger.invIfChanged(state.SWCHB_W, oldstate.SWCHB_W)
<< " 283/SWBCNT=" << myDebugger.invIfChanged(state.SWBCNT, oldstate.SWBCNT)
<< endl
// These are squirrely: some symbol files will define these as
// 0x284-0x287. Doesn't actually matter, these registers repeat
// every 16 bytes.
<< "294/TIM1T=" << myDebugger.invIfChanged(state.TIM1T, oldstate.TIM1T)
<< " 295/TIM8T=" << myDebugger.invIfChanged(state.TIM8T, oldstate.TIM8T)
<< " 296/TIM64T=" << myDebugger.invIfChanged(state.TIM64T, oldstate.TIM64T)
<< " 297/T1024T=" << myDebugger.invIfChanged(state.T1024T, oldstate.T1024T)
<< endl
<< "0x284/INTIM=" << myDebugger.invIfChanged(state.INTIM, oldstate.INTIM)
<< " 285/TIMINT=" << myDebugger.invIfChanged(state.TIMINT, oldstate.TIMINT)
<< " Timer_Clocks=" << myDebugger.invIfChanged(state.TIMCLKS, oldstate.TIMCLKS)
<< " INTIM_Clocks=" << myDebugger.invIfChanged(state.INTIMCLKS, oldstate.INTIMCLKS)
<< endl
<< "Left/P0diff: " << diffP0String() << " Right/P1diff: " << diffP0String()
<< endl
<< "TVType: " << tvTypeString() << " Switches: " << switchesString()
<< endl
// Yes, the fire buttons are in the TIA, but we might as well
// show them here for convenience.
<< "Left/P0 stick: " << dirP0String()
<< ((mySystem.peek(0x03c) & 0x80) ? "" : "(button) ")
<< endl
<< "Right/P1 stick: " << dirP1String()
<< ((mySystem.peek(0x03d) & 0x80) ? "" : "(button) ");
return buf.str();
}
+105
View File
@@ -0,0 +1,105 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RiotDebug.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef RIOT_DEBUG_HXX
#define RIOT_DEBUG_HXX
class Debugger;
class RiotDebug;
#include "Array.hxx"
#include "M6532.hxx"
#include "DebuggerSystem.hxx"
class RiotState : public DebuggerState
{
public:
uInt8 SWCHA_R, SWCHA_W, SWACNT, SWCHB_R, SWCHB_W, SWBCNT;
BoolArray swchaReadBits;
BoolArray swchaWriteBits;
BoolArray swacntBits;
BoolArray swchbReadBits;
BoolArray swchbWriteBits;
BoolArray swbcntBits;
uInt8 TIM1T, TIM8T, TIM64T, T1024T, INTIM, TIMINT;
Int32 TIMCLKS, INTIMCLKS;
// These are actually from the TIA, but are I/O related
uInt8 INPT0, INPT1, INPT2, INPT3, INPT4, INPT5;
};
class RiotDebug : public DebuggerSystem
{
public:
RiotDebug(Debugger& dbg, Console& console);
const DebuggerState& getState();
const DebuggerState& getOldState() { return myOldState; }
void saveOldState();
string toString();
/* Port A and B registers */
uInt8 swcha(int newVal = -1);
uInt8 swacnt(int newVal = -1);
uInt8 swchb(int newVal = -1);
uInt8 swbcnt(int newVal = -1);
/* TIA INPTx and VBLANK registers
Techically not part of the RIOT, but more appropriately placed here */
uInt8 inpt(int x);
bool vblank(int bit);
/* Timer registers & associated clock */
uInt8 tim1T(int newVal = -1);
uInt8 tim8T(int newVal = -1);
uInt8 tim64T(int newVal = -1);
uInt8 tim1024T(int newVal = -1);
uInt8 intim() const { return mySystem.m6532().intim(); }
uInt8 timint() const { return mySystem.m6532().timint(); }
Int32 timClocks() const { return mySystem.m6532().timerClocks(); }
Int32 intimClocks() const { return mySystem.m6532().intimClocks(); }
/* Controller ports */
Controller& controller(Controller::Jack jack) const;
/* Console switches */
bool diffP0(int newVal = -1);
bool diffP1(int newVal = -1);
bool tvType(int newVal = -1);
bool select(int newVal = -1);
bool reset(int newVal = -1);
/* Port A description */
string dirP0String();
string dirP1String();
/* Port B description */
string diffP0String();
string diffP1String();
string tvTypeString();
string switchesString();
private:
RiotState myState;
RiotState myOldState;
};
#endif
+852
View File
@@ -0,0 +1,852 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: TIADebug.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Base.hxx"
#include "System.hxx"
#include "Debugger.hxx"
#include "TIA.hxx"
#include "TIADebug.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIADebug::TIADebug(Debugger& dbg, Console& console)
: DebuggerSystem(dbg, console),
myTIA(console.tia())
{
nusizStrings[0] = "1 copy";
nusizStrings[1] = "2 copies - close (8)";
nusizStrings[2] = "2 copies - med (24)";
nusizStrings[3] = "3 copies - close (8)";
nusizStrings[4] = "2 copies - wide (56)";
nusizStrings[5] = "2x (16) sized player";
nusizStrings[6] = "3 copies - med (24)";
nusizStrings[7] = "4x (32) sized player";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const DebuggerState& TIADebug::getState()
{
myState.ram.clear();
for(int i = 0; i < 0x010; ++i)
myState.ram.push_back(myTIA.peek(i));
// Color registers
myState.coluRegs.clear();
myState.coluRegs.push_back(coluP0());
myState.coluRegs.push_back(coluP1());
myState.coluRegs.push_back(coluPF());
myState.coluRegs.push_back(coluBK());
// Player 1 & 2 graphics registers
myState.gr.clear();
myState.gr.push_back(grP0());
myState.gr.push_back(grP1());
// Position registers
myState.pos.clear();
myState.pos.push_back(posP0());
myState.pos.push_back(posP1());
myState.pos.push_back(posM0());
myState.pos.push_back(posM1());
myState.pos.push_back(posBL());
// Horizontal move registers
myState.hm.clear();
myState.hm.push_back(hmP0());
myState.hm.push_back(hmP1());
myState.hm.push_back(hmM0());
myState.hm.push_back(hmM1());
myState.hm.push_back(hmBL());
// Playfield registers
myState.pf.clear();
myState.pf.push_back(pf0());
myState.pf.push_back(pf1());
myState.pf.push_back(pf2());
// Size registers
myState.size.clear();
myState.size.push_back(nusizP0());
myState.size.push_back(nusizP1());
myState.size.push_back(nusizM0());
myState.size.push_back(nusizM1());
myState.size.push_back(sizeBL());
// Audio registers
myState.aud.clear();
myState.aud.push_back(audF0());
myState.aud.push_back(audF1());
myState.aud.push_back(audC0());
myState.aud.push_back(audC1());
myState.aud.push_back(audV0());
myState.aud.push_back(audV1());
return myState;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIADebug::saveOldState()
{
myOldState.ram.clear();
for(int i = 0; i < 0x010; ++i)
myOldState.ram.push_back(myTIA.peek(i));
// Color registers
myOldState.coluRegs.clear();
myOldState.coluRegs.push_back(coluP0());
myOldState.coluRegs.push_back(coluP1());
myOldState.coluRegs.push_back(coluPF());
myOldState.coluRegs.push_back(coluBK());
// Player 1 & 2 graphics registers
myOldState.gr.clear();
myOldState.gr.push_back(grP0());
myOldState.gr.push_back(grP1());
// Position registers
myOldState.pos.clear();
myOldState.pos.push_back(posP0());
myOldState.pos.push_back(posP1());
myOldState.pos.push_back(posM0());
myOldState.pos.push_back(posM1());
myOldState.pos.push_back(posBL());
// Horizontal move registers
myOldState.hm.clear();
myOldState.hm.push_back(hmP0());
myOldState.hm.push_back(hmP1());
myOldState.hm.push_back(hmM0());
myOldState.hm.push_back(hmM1());
myOldState.hm.push_back(hmBL());
// Playfield registers
myOldState.pf.clear();
myOldState.pf.push_back(pf0());
myOldState.pf.push_back(pf1());
myOldState.pf.push_back(pf2());
// Size registers
myOldState.size.clear();
myOldState.size.push_back(nusizP0());
myOldState.size.push_back(nusizP1());
myOldState.size.push_back(nusizM0());
myOldState.size.push_back(nusizM1());
myOldState.size.push_back(sizeBL());
// Audio registers
myOldState.aud.clear();
myOldState.aud.push_back(audF0());
myOldState.aud.push_back(audF1());
myOldState.aud.push_back(audC0());
myOldState.aud.push_back(audC1());
myOldState.aud.push_back(audV0());
myOldState.aud.push_back(audV1());
}
/* the set methods now use mySystem.poke(). This will save us the
trouble of masking the values here, since TIA::poke() will do it
for us.
This means that the GUI should *never* just display the value the
user entered: it should always read the return value of the set
method and display that.
An Example:
User enters "ff" in the AUDV0 field. GUI calls value = tiaDebug->audV0(0xff).
The AUDV0 register is only 4 bits wide, so "value" is 0x0f. That's what
should be displayed.
In a perfect world, the GUI would only allow one hex digit to be entered...
but we allow decimal or binary input in the GUI (with # or \ prefix). The
only way to make that work would be to validate the data entry after every
keystroke... which would be a pain for both us and the user. Using poke()
here is a compromise that allows the TIA to do the range-checking for us,
so the GUI and/or TIADebug don't have to duplicate logic from TIA::poke().
*/
// bool vdelP0(int newVal = -1);
// bool vdelP1(int newVal = -1);
// bool vdelBL(int newVal = -1);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vdelP0(int newVal)
{
if(newVal > -1)
mySystem.poke(VDELP0, ((bool)newVal));
return myTIA.myVDELP0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vdelP1(int newVal)
{
if(newVal > -1)
mySystem.poke(VDELP1, ((bool)newVal));
return myTIA.myVDELP1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vdelBL(int newVal)
{
if(newVal > -1)
mySystem.poke(VDELBL, ((bool)newVal));
return myTIA.myVDELBL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::enaM0(int newVal)
{
if(newVal > -1)
mySystem.poke(ENAM0, ((bool)newVal) << 1);
return myTIA.myENAM0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::enaM1(int newVal)
{
if(newVal > -1)
mySystem.poke(ENAM1, ((bool)newVal) << 1);
return myTIA.myENAM1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::enaBL(int newVal)
{
if(newVal > -1)
mySystem.poke(ENABL, ((bool)newVal) << 1);
return myTIA.myENABL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::resMP0(int newVal)
{
if(newVal > -1)
mySystem.poke(RESMP0, ((bool)newVal) << 1);
return myTIA.myRESMP0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::resMP1(int newVal)
{
if(newVal > -1)
mySystem.poke(RESMP1, ((bool)newVal) << 1);
return myTIA.myRESMP1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::refP0(int newVal)
{
if(newVal > -1)
mySystem.poke(REFP0, ((bool)newVal) << 3);
return myTIA.myREFP0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::refP1(int newVal)
{
if(newVal > -1)
mySystem.poke(REFP1, ((bool)newVal) << 3);
return myTIA.myREFP1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::refPF(int newVal)
{
if(newVal > -1)
{
int tmp = myTIA.myCTRLPF;
if(newVal)
tmp |= 0x01;
else
tmp &= ~0x01;
mySystem.poke(CTRLPF, tmp);
}
return myTIA.myCTRLPF & 0x01;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::scorePF(int newVal)
{
if(newVal > -1)
{
int tmp = myTIA.myCTRLPF;
if(newVal)
tmp |= 0x02;
else
tmp &= ~0x02;
mySystem.poke(CTRLPF, tmp);
}
return myTIA.myCTRLPF & 0x02;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::priorityPF(int newVal)
{
if(newVal > -1)
{
int tmp = myTIA.myCTRLPF;
if(newVal)
tmp |= 0x04;
else
tmp &= ~0x04;
mySystem.poke(CTRLPF, tmp);
}
return myTIA.myCTRLPF & 0x04;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::collision(int collID, int newVal)
{
unsigned int mask = 1 << collID;
if(newVal > -1)
{
if(newVal)
myTIA.myCollision |= mask;
else
myTIA.myCollision &= ~mask;
}
return myTIA.myCollision & mask;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::audC0(int newVal)
{
if(newVal > -1)
mySystem.poke(AUDC0, newVal);
return myTIA.myAUDC0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::audC1(int newVal)
{
if(newVal > -1)
mySystem.poke(AUDC1, newVal);
return myTIA.myAUDC1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::audV0(int newVal)
{
if(newVal > -1)
mySystem.poke(AUDV0, newVal);
return myTIA.myAUDV0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::audV1(int newVal)
{
if(newVal > -1)
mySystem.poke(AUDV1, newVal);
return myTIA.myAUDV1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::audF0(int newVal)
{
if(newVal > -1)
mySystem.poke(AUDF0, newVal);
return myTIA.myAUDF0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::audF1(int newVal)
{
if(newVal > -1)
mySystem.poke(AUDF1, newVal);
return myTIA.myAUDF1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::pf0(int newVal)
{
if(newVal > -1)
mySystem.poke(PF0, newVal << 4);
return myTIA.myPF & 0x0f;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::pf1(int newVal)
{
if(newVal > -1)
mySystem.poke(PF1, newVal);
return (myTIA.myPF & 0xff0) >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::pf2(int newVal)
{
if(newVal > -1)
mySystem.poke(PF2, newVal);
return (myTIA.myPF & 0xff000) >> 12;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::coluP0(int newVal)
{
if(newVal > -1)
mySystem.poke(COLUP0, newVal);
return myTIA.myColor[P0Color] & 0xff;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::coluP1(int newVal)
{
if(newVal > -1)
mySystem.poke(COLUP1, newVal);
return myTIA.myColor[P1Color] & 0xff;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::coluPF(int newVal)
{
if(newVal > -1)
mySystem.poke(COLUPF, newVal);
return myTIA.myColor[PFColor] & 0xff;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::coluBK(int newVal)
{
if(newVal > -1)
mySystem.poke(COLUBK, newVal);
return myTIA.myColor[BKColor] & 0xff;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::nusiz0(int newVal)
{
if(newVal > -1)
mySystem.poke(NUSIZ0, newVal);
return myTIA.myNUSIZ0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::nusiz1(int newVal)
{
if(newVal > -1)
mySystem.poke(NUSIZ1, newVal);
return myTIA.myNUSIZ1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::nusizP0(int newVal)
{
if(newVal > -1)
{
uInt8 tmp = myTIA.myNUSIZ0 & ~0x07;
tmp |= (newVal & 0x07);
mySystem.poke(NUSIZ0, tmp);
}
return myTIA.myNUSIZ0 & 0x07;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::nusizP1(int newVal)
{
if(newVal > -1)
{
uInt8 tmp = myTIA.myNUSIZ1 & ~0x07;
tmp |= newVal & 0x07;
mySystem.poke(NUSIZ1, tmp);
}
return myTIA.myNUSIZ1 & 0x07;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::nusizM0(int newVal)
{
if(newVal > -1)
{
uInt8 tmp = myTIA.myNUSIZ0 & ~0x30;
tmp |= (newVal & 0x04) << 4;
mySystem.poke(NUSIZ0, tmp);
}
return (myTIA.myNUSIZ0 & 0x30) >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::nusizM1(int newVal)
{
if(newVal > -1)
{
uInt8 tmp = myTIA.myNUSIZ1 & ~0x30;
tmp |= (newVal & 0x04) << 4;
mySystem.poke(NUSIZ1, tmp);
}
return (myTIA.myNUSIZ1 & 0x30) >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::grP0(int newVal)
{
if(newVal > -1)
mySystem.poke(GRP0, newVal);
return myTIA.myGRP0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::grP1(int newVal)
{
if(newVal > -1)
mySystem.poke(GRP1, newVal);
return myTIA.myGRP1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::posP0(int newVal)
{
if(newVal > -1)
myTIA.myPOSP0 = newVal;
return myTIA.myPOSP0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::posP1(int newVal)
{
if(newVal > -1)
myTIA.myPOSP1 = newVal;
return myTIA.myPOSP1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::posM0(int newVal)
{
if(newVal > -1)
myTIA.myPOSM0 = newVal;
return myTIA.myPOSM0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::posM1(int newVal)
{
if(newVal > -1)
myTIA.myPOSM1 = newVal;
return myTIA.myPOSM1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::posBL(int newVal)
{
if(newVal > -1)
myTIA.myPOSBL = newVal;
return myTIA.myPOSBL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::ctrlPF(int newVal)
{
if(newVal > -1)
mySystem.poke(CTRLPF, newVal);
return myTIA.myCTRLPF;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::sizeBL(int newVal)
{
if(newVal > -1)
{
uInt8 tmp = myTIA.myCTRLPF & ~0x30;
tmp |= (newVal & 0x04) << 4;
mySystem.poke(CTRLPF, tmp);
}
return (myTIA.myCTRLPF & 0x30) >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::hmP0(int newVal)
{
if(newVal > -1)
mySystem.poke(HMP0, newVal << 4);
return myTIA.myHMP0 >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::hmP1(int newVal)
{
if(newVal > -1)
mySystem.poke(HMP1, newVal << 4);
return myTIA.myHMP1 >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::hmM0(int newVal)
{
if(newVal > -1)
mySystem.poke(HMM0, newVal << 4);
return myTIA.myHMM0 >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::hmM1(int newVal)
{
if(newVal > -1)
mySystem.poke(HMM1, newVal << 4);
return myTIA.myHMM1 >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIADebug::hmBL(int newVal)
{
if(newVal > -1)
mySystem.poke(HMBL, newVal << 4);
return myTIA.myHMBL >> 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::frameCount()
{
return myTIA.myFrameCounter;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::scanlines()
{
return myTIA.scanlines();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::clocksThisLine()
{
return myTIA.clocksThisLine();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vsync()
{
return (myTIA.myVSYNC & 2) == 2;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vblank()
{
return (myTIA.myVBLANK & 2) == 2;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TIADebug::colorSwatch(uInt8 c)
{
string ret;
ret += char((c >> 1) | 0x80);
ret += "\177 ";
ret += "\177\003 ";
return ret;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TIADebug::audFreq(uInt8 div)
{
string ret;
char buf[10];
double hz = 30000.0;
if(div) hz /= div;
BSPF_snprintf(buf, 9, "%5.1f", hz);
ret += buf;
ret += "Hz";
return ret;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TIADebug::booleanWithLabel(string label, bool value)
{
char buf[64];
string ret;
if(value)
{
char *p = buf;
const char *q = label.c_str();
while((*p++ = toupper(*q++)))
;
ret += "+";
ret += buf;
return ret;
}
else
return "-" + label;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TIADebug::toString()
{
ostringstream buf;
buf << "00: ";
for (uInt8 j = 0; j < 0x010; j++)
{
buf << Common::Base::HEX2 << (int)mySystem.peek(j) << " ";
if(j == 0x07) buf << "- ";
}
buf << endl;
// TODO: inverse video for changed regs. Core needs to track this.
// TODO: strobes? WSYNC RSYNC RESP0/1 RESM0/1 RESBL HMOVE HMCLR CXCLR
const TiaState& state = (TiaState&) getState();
// const TiaState& oldstate = (TiaState&) getOldState();
// build up output, then return it.
buf << "scanline " << dec << myTIA.scanlines() << " "
<< booleanWithLabel("vsync", vsync()) << " "
<< booleanWithLabel("vblank", vblank())
<< endl
<< booleanWithLabel("inpt0", myTIA.peek(0x08) & 0x80) << " "
<< booleanWithLabel("inpt1", myTIA.peek(0x09) & 0x80) << " "
<< booleanWithLabel("inpt2", myTIA.peek(0x0a) & 0x80) << " "
<< booleanWithLabel("inpt3", myTIA.peek(0x0b) & 0x80) << " "
<< booleanWithLabel("inpt4", myTIA.peek(0x0c) & 0x80) << " "
<< booleanWithLabel("inpt5", myTIA.peek(0x0d) & 0x80) << " "
<< booleanWithLabel("dump_gnd_0123", myTIA.myDumpEnabled)
<< endl
<< "COLUxx: "
<< "P0=$" << Common::Base::HEX2 << state.coluRegs[0] << "/"
<< colorSwatch(state.coluRegs[0])
<< "P1=$" << Common::Base::HEX2 << state.coluRegs[1] << "/"
<< colorSwatch(state.coluRegs[1])
<< "PF=$" << Common::Base::HEX2 << state.coluRegs[2] << "/"
<< colorSwatch(state.coluRegs[2])
<< "BK=$" << Common::Base::HEX2 << state.coluRegs[3] << "/"
<< colorSwatch(state.coluRegs[3])
<< endl
<< "P0: GR=%" << Common::Base::toString(state.gr[P0], Common::Base::F_2_8)
<< " pos=#" << dec << state.pos[P0]
<< " HM=$" << Common::Base::HEX2 << state.hm[P0] << " "
<< nusizP0String() << " "
<< booleanWithLabel("refl", refP0()) << " "
<< booleanWithLabel("delay", vdelP0())
<< endl
<< "P1: GR=%" << Common::Base::toString(state.gr[P1], Common::Base::F_2_8)
<< " pos=#" << dec << state.pos[P1]
<< " HM=$" << Common::Base::HEX2 << state.hm[P1] << " "
<< nusizP1String() << " "
<< booleanWithLabel("refl", refP1()) << " "
<< booleanWithLabel("delay", vdelP1())
<< endl
<< "M0: " << (myTIA.myENAM0 ? " ENABLED" : "disabled")
<< " pos=#" << dec << state.pos[M0]
<< " HM=$" << Common::Base::HEX2 << state.hm[M0]
<< " size=" << dec << state.size[M0] << " "
<< booleanWithLabel("reset", resMP0())
<< endl
<< "M1: " << (myTIA.myENAM1 ? " ENABLED" : "disabled")
<< " pos=#" << dec << state.pos[M1]
<< " HM=$" << Common::Base::HEX2 << state.hm[M1]
<< " size=" << dec << state.size[M1] << " "
<< booleanWithLabel("reset", resMP0())
<< endl
<< "BL: " << (myTIA.myENABL ? " ENABLED" : "disabled")
<< " pos=#" << dec << state.pos[BL]
<< " HM=$" << Common::Base::HEX2 << state.hm[BL]
<< " size=" << dec << state.size[BL] << " "
<< booleanWithLabel("delay", vdelBL())
<< endl
<< "PF0: %" << Common::Base::toString(state.pf[0], Common::Base::F_2_8) << "/$"
<< Common::Base::HEX2 << state.pf[0]
<< " PF1: %" << Common::Base::toString(state.pf[1], Common::Base::F_2_8) << "/$"
<< Common::Base::HEX2 << state.pf[1]
<< " PF2: %" << Common::Base::toString(state.pf[2], Common::Base::F_2_8) << "/$"
<< Common::Base::HEX2 << state.pf[2]
<< endl << " "
<< booleanWithLabel("reflect", refPF()) << " "
<< booleanWithLabel("score", scorePF()) << " "
<< booleanWithLabel("priority", priorityPF())
<< endl
<< "Collisions: "
<< booleanWithLabel("m0_p1 ", collM0_P1())
<< booleanWithLabel("m0_p0 ", collM0_P0())
<< booleanWithLabel("m1_p0 ", collM1_P0())
<< booleanWithLabel("m1_p1 ", collM1_P1())
<< booleanWithLabel("p0_pf ", collP0_PF())
<< booleanWithLabel("p0_bl ", collP0_BL())
<< booleanWithLabel("p1_pf ", collP1_PF())
<< endl << " "
<< booleanWithLabel("p1_bl ", collP1_BL())
<< booleanWithLabel("m0_pf ", collM0_PF())
<< booleanWithLabel("m0_bl ", collM0_BL())
<< booleanWithLabel("m1_pf ", collM1_PF())
<< booleanWithLabel("m1_bl ", collM1_BL())
<< booleanWithLabel("bl_pf ", collBL_PF())
<< booleanWithLabel("p0_p1 ", collP0_P1())
<< endl << " "
<< booleanWithLabel("m0_m1 ", collM0_M1())
<< endl
<< "AUDF0: $" << Common::Base::HEX2 << (int)myTIA.myAUDF0
<< "/" << audFreq(myTIA.myAUDF0) << " "
<< "AUDC0: $" << Common::Base::HEX2 << (int)myTIA.myAUDC0 << " "
<< "AUDV0: $" << Common::Base::HEX2 << (int)myTIA.myAUDV0
<< endl
<< "AUDF1: $" << Common::Base::HEX2 << (int)myTIA.myAUDF1
<< "/" << audFreq(myTIA.myAUDF1) << " "
<< "AUDC1: $" << Common::Base::HEX2 << (int)myTIA.myAUDC1 << " "
<< "AUDV1: $" << Common::Base::HEX2 << (int)myTIA.myAUDV1
;
// note: last line should not contain \n, caller will add.
return buf.str();
}
+185
View File
@@ -0,0 +1,185 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: TIADebug.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef TIA_DEBUG_HXX
#define TIA_DEBUG_HXX
class Debugger;
class TiaDebug;
class TIA;
#include "Array.hxx"
#include "DebuggerSystem.hxx"
// pointer types for TIADebug instance methods
// (used by TiaMethodExpression)
class TIADebug;
typedef int (TIADebug::*TIADEBUG_INT_METHOD)();
// call the pointed-to method on the (global) debugger object.
#define CALL_TIADEBUG_METHOD(method) ( ( Debugger::debugger().tiaDebug().*method)() )
// Indices for various IntArray in TiaState
enum {
P0, P1, M0, M1, BL
};
class TiaState : public DebuggerState
{
public:
IntArray ram;
IntArray coluRegs;
IntArray gr;
IntArray pos;
IntArray hm;
IntArray pf;
IntArray size;
IntArray aud;
};
class TIADebug : public DebuggerSystem
{
public:
TIADebug(Debugger& dbg, Console& console);
TIA& tia() const { return myTIA; }
const DebuggerState& getState();
const DebuggerState& getOldState() { return myOldState; }
void saveOldState();
string toString();
// TIA byte (or part of a byte) registers
uInt8 nusiz0(int newVal = -1);
uInt8 nusiz1(int newVal = -1);
uInt8 nusizP0(int newVal = -1);
uInt8 nusizP1(int newVal = -1);
uInt8 nusizM0(int newVal = -1);
uInt8 nusizM1(int newVal = -1);
const string& nusizP0String() { return nusizStrings[nusizP0()]; }
const string& nusizP1String() { return nusizStrings[nusizP1()]; }
uInt8 coluP0(int newVal = -1);
uInt8 coluP1(int newVal = -1);
uInt8 coluPF(int newVal = -1);
uInt8 coluBK(int newVal = -1);
uInt8 sizeBL(int newVal = -1);
uInt8 ctrlPF(int newVal = -1);
uInt8 pf0(int newVal = -1);
uInt8 pf1(int newVal = -1);
uInt8 pf2(int newVal = -1);
uInt8 grP0(int newVal = -1);
uInt8 grP1(int newVal = -1);
uInt8 posP0(int newVal = -1);
uInt8 posP1(int newVal = -1);
uInt8 posM0(int newVal = -1);
uInt8 posM1(int newVal = -1);
uInt8 posBL(int newVal = -1);
uInt8 hmP0(int newVal = -1);
uInt8 hmP1(int newVal = -1);
uInt8 hmM0(int newVal = -1);
uInt8 hmM1(int newVal = -1);
uInt8 hmBL(int newVal = -1);
uInt8 audC0(int newVal = -1);
uInt8 audC1(int newVal = -1);
uInt8 audF0(int newVal = -1);
uInt8 audF1(int newVal = -1);
uInt8 audV0(int newVal = -1);
uInt8 audV1(int newVal = -1);
// TIA bool registers
bool refP0(int newVal = -1);
bool refP1(int newVal = -1);
bool enaM0(int newVal = -1);
bool enaM1(int newVal = -1);
bool enaBL(int newVal = -1);
bool vdelP0(int newVal = -1);
bool vdelP1(int newVal = -1);
bool vdelBL(int newVal = -1);
bool resMP0(int newVal = -1);
bool resMP1(int newVal = -1);
bool refPF(int newVal = -1);
bool scorePF(int newVal = -1);
bool priorityPF(int newVal = -1);
// Collision registers
bool collM0_P1(int newVal = -1) { return collision(0, newVal); }
bool collM0_P0(int newVal = -1) { return collision(1, newVal); }
bool collM1_P0(int newVal = -1) { return collision(2, newVal); }
bool collM1_P1(int newVal = -1) { return collision(3, newVal); }
bool collP0_PF(int newVal = -1) { return collision(4, newVal); }
bool collP0_BL(int newVal = -1) { return collision(5, newVal); }
bool collP1_PF(int newVal = -1) { return collision(6, newVal); }
bool collP1_BL(int newVal = -1) { return collision(7, newVal); }
bool collM0_PF(int newVal = -1) { return collision(8, newVal); }
bool collM0_BL(int newVal = -1) { return collision(9, newVal); }
bool collM1_PF(int newVal = -1) { return collision(10, newVal); }
bool collM1_BL(int newVal = -1) { return collision(11, newVal); }
bool collBL_PF(int newVal = -1) { return collision(12, newVal); }
bool collP0_P1(int newVal = -1) { return collision(13, newVal); }
bool collM0_M1(int newVal = -1) { return collision(14, newVal); }
// TIA strobe registers
void strobeWsync() { mySystem.poke(WSYNC, 0); }
void strobeRsync() { mySystem.poke(RSYNC, 0); } // not emulated!
void strobeResP0() { mySystem.poke(RESP0, 0); }
void strobeResP1() { mySystem.poke(RESP1, 0); }
void strobeResM0() { mySystem.poke(RESM0, 0); }
void strobeResM1() { mySystem.poke(RESM1, 0); }
void strobeResBL() { mySystem.poke(RESBL, 0); }
void strobeHmove() { mySystem.poke(HMOVE, 0); }
void strobeHmclr() { mySystem.poke(HMCLR, 0); }
void strobeCxclr() { mySystem.poke(CXCLR, 0); }
// Read-only internal TIA state
int scanlines();
int frameCount();
int clocksThisLine();
bool vsync();
bool vblank();
int vsyncAsInt() { return int(vsync()); } // so we can use _vsync pseudo-register
int vblankAsInt() { return int(vblank()); } // so we can use _vblank pseudo-register
private:
/** Display a color patch for color at given index in the palette */
string colorSwatch(uInt8 c);
/** Get/set specific bits in the collision register (used by collXX_XX) */
bool collision(int collID, int newVal);
string audFreq(uInt8 div);
string booleanWithLabel(string label, bool value);
private:
TiaState myState;
TiaState myOldState;
TIA& myTIA;
string nusizStrings[8];
};
#endif
+138
View File
@@ -0,0 +1,138 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: AudioWidget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "DataGridWidget.hxx"
#include "FrameBuffer.hxx"
#include "GuiObject.hxx"
#include "OSystem.hxx"
#include "TIADebug.hxx"
#include "Widget.hxx"
#include "AudioWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AudioWidget::AudioWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h)
: Widget(boss, lfont, x, y, w, h),
CommandSender(boss)
{
const int fontWidth = lfont.getMaxCharWidth(),
fontHeight = lfont.getFontHeight(),
lineHeight = lfont.getLineHeight();
int xpos = 10, ypos = 25, lwidth = lfont.getStringWidth("AUDW: ");
// AudF registers
new StaticTextWidget(boss, lfont, xpos, ypos+2,
lwidth, fontHeight,
"AUDF:", kTextAlignLeft);
xpos += lwidth;
myAudF = new DataGridWidget(boss, nfont, xpos, ypos,
2, 1, 2, 5, Common::Base::F_16);
myAudF->setTarget(this);
myAudF->setID(kAUDFID);
myAudF->setEditable(false);
addFocusWidget(myAudF);
for(int col = 0; col < 2; ++col)
{
new StaticTextWidget(boss, lfont, xpos + col*myAudF->colWidth() + 7,
ypos - lineHeight, fontWidth, fontHeight,
Common::Base::toString(col, Common::Base::F_16_1),
kTextAlignLeft);
}
// AudC registers
xpos = 10; ypos += lineHeight + 5;
new StaticTextWidget(boss, lfont, xpos, ypos+2, lwidth, fontHeight,
"AUDC:", kTextAlignLeft);
xpos += lwidth;
myAudC = new DataGridWidget(boss, nfont, xpos, ypos,
2, 1, 2, 4, Common::Base::F_16);
myAudC->setTarget(this);
myAudC->setID(kAUDCID);
myAudC->setEditable(false);
addFocusWidget(myAudC);
// AudV registers
xpos = 10; ypos += lineHeight + 5;
new StaticTextWidget(boss, lfont, xpos, ypos+2, lwidth, fontHeight,
"AUDV:", kTextAlignLeft);
xpos += lwidth;
myAudV = new DataGridWidget(boss, nfont, xpos, ypos,
2, 1, 2, 4, Common::Base::F_16);
myAudV->setTarget(this);
myAudV->setID(kAUDVID);
myAudV->setEditable(false);
addFocusWidget(myAudV);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AudioWidget::~AudioWidget()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
// TODO - implement this
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioWidget::loadConfig()
{
IntArray alist;
IntArray vlist;
BoolArray blist, changed, grNew, grOld;
Debugger& dbg = instance().debugger();
TIADebug& tia = dbg.tiaDebug();
TiaState state = (TiaState&) tia.getState();
TiaState oldstate = (TiaState&) tia.getOldState();
// AUDF0/1
alist.clear(); vlist.clear(); changed.clear();
for(unsigned int i = 0; i < 2; i++)
{
alist.push_back(i);
vlist.push_back(state.aud[i]);
changed.push_back(state.aud[i] != oldstate.aud[i]);
}
myAudF->setList(alist, vlist, changed);
// AUDC0/1
alist.clear(); vlist.clear(); changed.clear();
for(unsigned int i = 2; i < 4; i++)
{
alist.push_back(i-2);
vlist.push_back(state.aud[i]);
changed.push_back(state.aud[i] != oldstate.aud[i]);
}
myAudC->setList(alist, vlist, changed);
// AUDV0/1
alist.clear(); vlist.clear(); changed.clear();
for(unsigned int i = 4; i < 6; i++)
{
alist.push_back(i-4);
vlist.push_back(state.aud[i]);
changed.push_back(state.aud[i] != oldstate.aud[i]);
}
myAudV->setList(alist, vlist, changed);
}
+55
View File
@@ -0,0 +1,55 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: AudioWidget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef AUDIO_WIDGET_HXX
#define AUDIO_WIDGET_HXX
class GuiObject;
class DataGridWidget;
#include "Widget.hxx"
#include "Command.hxx"
class AudioWidget : public Widget, public CommandSender
{
public:
AudioWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h);
virtual ~AudioWidget();
private:
// ID's for the various widgets
// We need ID's, since there are more than one of several types of widgets
enum {
kAUDFID,
kAUDCID,
kAUDVID
};
void handleCommand(CommandSender* sender, int cmd, int data, int id);
void loadConfig();
private:
DataGridWidget* myAudF;
DataGridWidget* myAudC;
DataGridWidget* myAudV;
};
#endif
+129
View File
@@ -0,0 +1,129 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: BoosterWidget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "OSystem.hxx"
#include "EventHandler.hxx"
#include "BoosterWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BoosterWidget::BoosterWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (Booster):" : "Right (Booster):";
const int fontHeight = font.getFontHeight();
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Booster):");
StaticTextWidget* t;
t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft);
xpos += t->getWidth()/2 - 5; ypos += t->getHeight() + 10;
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJUp]->setID(kJUp);
myPins[kJUp]->setTarget(this);
ypos += myPins[kJUp]->getHeight() * 2 + 10;
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJDown]->setID(kJDown);
myPins[kJDown]->setTarget(this);
xpos -= myPins[kJUp]->getWidth() + 5;
ypos -= myPins[kJUp]->getHeight() + 5;
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJLeft]->setID(kJLeft);
myPins[kJLeft]->setTarget(this);
xpos += (myPins[kJUp]->getWidth() + 5) * 2;
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJRight]->setID(kJRight);
myPins[kJRight]->setTarget(this);
xpos -= (myPins[kJUp]->getWidth() + 5) * 2;
ypos = 20 + (myPins[kJUp]->getHeight() + 10) * 3;
myPins[kJFire] = new CheckboxWidget(boss, font, xpos, ypos, "Fire", kCheckActionCmd);
myPins[kJFire]->setID(kJFire);
myPins[kJFire]->setTarget(this);
ypos += myPins[kJFire]->getHeight() + 5;
myPins[kJBooster] = new CheckboxWidget(boss, font, xpos, ypos, "Booster", kCheckActionCmd);
myPins[kJBooster]->setID(kJBooster);
myPins[kJBooster]->setTarget(this);
ypos += myPins[kJBooster]->getHeight() + 5;
myPins[kJTrigger] = new CheckboxWidget(boss, font, xpos, ypos, "Trigger", kCheckActionCmd);
myPins[kJTrigger]->setID(kJTrigger);
myPins[kJTrigger]->setTarget(this);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BoosterWidget::~BoosterWidget()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BoosterWidget::loadConfig()
{
myPins[kJUp]->setState(!myController.read(ourPinNo[kJUp]));
myPins[kJDown]->setState(!myController.read(ourPinNo[kJDown]));
myPins[kJLeft]->setState(!myController.read(ourPinNo[kJLeft]));
myPins[kJRight]->setState(!myController.read(ourPinNo[kJRight]));
myPins[kJFire]->setState(!myController.read(ourPinNo[kJFire]));
myPins[kJBooster]->setState(
myController.read(Controller::Five) == Controller::minimumResistance);
myPins[kJTrigger]->setState(
myController.read(Controller::Nine) == Controller::minimumResistance);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BoosterWidget::handleCommand(
CommandSender* sender, int cmd, int data, int id)
{
if(cmd == kCheckActionCmd)
{
switch(id)
{
case kJUp:
case kJDown:
case kJLeft:
case kJRight:
case kJFire:
myController.set(ourPinNo[id], !myPins[id]->getState());
break;
case kJBooster:
myController.set(Controller::Five,
myPins[id]->getState() ? Controller::minimumResistance :
Controller::maximumResistance);
break;
case kJTrigger:
myController.set(Controller::Nine,
myPins[id]->getState() ? Controller::minimumResistance :
Controller::maximumResistance);
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Controller::DigitalPin BoosterWidget::ourPinNo[5] = {
Controller::One, Controller::Two, Controller::Three, Controller::Four,
Controller::Six
};
+44
View File
@@ -0,0 +1,44 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: BoosterWidget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef BOOSTER_WIDGET_HXX
#define BOOSTER_WIDGET_HXX
#include "Control.hxx"
#include "Event.hxx"
#include "ControllerWidget.hxx"
class BoosterWidget : public ControllerWidget
{
public:
BoosterWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
Controller& controller);
virtual ~BoosterWidget();
void loadConfig();
void handleCommand(CommandSender* sender, int cmd, int data, int id);
private:
enum { kJUp = 0, kJDown, kJLeft, kJRight, kJFire, kJBooster, kJTrigger };
CheckboxWidget* myPins[7];
static Controller::DigitalPin ourPinNo[5];
};
#endif
+92
View File
@@ -0,0 +1,92 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart0840Widget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Cart0840.hxx"
#include "PopUpWidget.hxx"
#include "Cart0840Widget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge0840Widget::Cartridge0840Widget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge0840& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{
uInt16 size = 2 * 4096;
ostringstream info;
info << "0840 ECONObanking, two 4K banks\n"
<< "Startup bank = " << cart.myStartBank << "\n";
// Eventually, we should query this from the debugger/disassembler
for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2;
++i, offset += 0x1000, spot += 0x40)
{
uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset];
start -= start % 0x1000;
info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - "
<< "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n";
}
int xpos = 10,
ypos = addBaseInformation(size, "Fred X. Quimby", info.str()) + myLineHeight;
VariantList items;
items.push_back("0 ($800)");
items.push_back("1 ($840)");
myBank =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($800) "),
myLineHeight, items, "Set bank: ",
_font.getStringWidth("Set bank: "), kBankChanged);
myBank->setTarget(this);
addFocusWidget(myBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge0840Widget::loadConfig()
{
myBank->setSelectedIndex(myCart.myCurrentBank);
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge0840Widget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
if(cmd == kBankChanged)
{
myCart.unlockBank();
myCart.bank(myBank->getSelected());
myCart.lockBank();
invalidate();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge0840Widget::bankState()
{
ostringstream& buf = buffer();
static const char* spot[] = { "$800", "$840" };
buf << "Bank = " << dec << myCart.myCurrentBank
<< ", hotspot = " << spot[myCart.myCurrentBank];
return buf.str();
}
+49
View File
@@ -0,0 +1,49 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart0840Widget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CARTRIDGE0840_WIDGET_HXX
#define CARTRIDGE0840_WIDGET_HXX
class Cartridge0840;
class PopUpWidget;
#include "CartDebugWidget.hxx"
class Cartridge0840Widget : public CartDebugWidget
{
public:
Cartridge0840Widget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
Cartridge0840& cart);
virtual ~Cartridge0840Widget() { }
void loadConfig();
void handleCommand(CommandSender* sender, int cmd, int data, int id);
string bankState();
private:
Cartridge0840& myCart;
PopUpWidget* myBank;
enum { kBankChanged = 'bkCH' };
};
#endif
+38
View File
@@ -0,0 +1,38 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart2KWidget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Cart2K.hxx"
#include "Cart2KWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge2KWidget::Cartridge2KWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge2K& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h)
{
// Eventually, we should query this from the debugger/disassembler
uInt16 size = cart.mySize;
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
start -= start % size;
ostringstream info;
info << "Standard 2K cartridge, non-bankswitched\n"
<< "Accessible @ $" << Common::Base::HEX4 << start << " - " << "$" << (start + size - 1);
addBaseInformation(size, "Atari", info.str());
}
+40
View File
@@ -0,0 +1,40 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart2KWidget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CARTRIDGE2K_WIDGET_HXX
#define CARTRIDGE2K_WIDGET_HXX
class Cartridge2K;
#include "CartDebugWidget.hxx"
class Cartridge2KWidget : public CartDebugWidget
{
public:
Cartridge2KWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
Cartridge2K& cart);
virtual ~Cartridge2KWidget() { }
// No implementation for non-bankswitched ROMs
void loadConfig() { }
void handleCommand(CommandSender* sender, int cmd, int data, int id) { }
};
#endif
+159
View File
@@ -0,0 +1,159 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart3EWidget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Cart3E.hxx"
#include "PopUpWidget.hxx"
#include "Cart3EWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge3EWidget::Cartridge3EWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge3E& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart),
myNumRomBanks(cart.mySize >> 11),
myNumRamBanks(32)
{
uInt32 size = cart.mySize;
ostringstream info;
info << "3E cartridge - (3F + RAM)\n"
<< " 2-256 2K ROM (currently " << myNumRomBanks << "), 32 1K RAM\n"
<< "First 2K (ROM) selected by writing to $3F\n"
"First 2K (RAM) selected by writing to $3E\n"
" $F000 - $F3FF (R), $F400 - $F7FF (W)\n"
"Last 2K always points to last 2K of ROM\n";
if(cart.myStartBank < myNumRomBanks)
info << "Startup bank = " << cart.myStartBank << " (ROM)\n";
else
info << "Startup bank = " << (cart.myStartBank-myNumRomBanks) << " (RAM)\n";
// Eventually, we should query this from the debugger/disassembler
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
start -= start % 0x1000;
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
int xpos = 10,
ypos = addBaseInformation(size, "TigerVision", info.str()) + myLineHeight;
VariantList romitems;
for(uInt32 i = 0; i < myNumRomBanks; ++i)
romitems.push_back(i);
romitems.push_back("Inactive", "");
VariantList ramitems;
for(uInt32 i = 0; i < myNumRamBanks; ++i)
ramitems.push_back(i);
ramitems.push_back("Inactive", "");
ostringstream label;
label << "Set bank ($" << Common::Base::HEX4 << start << " - $"
<< (start+0x7FF) << "): ";
new StaticTextWidget(_boss, _font, xpos, ypos, _font.getStringWidth(label.str()),
myFontHeight, label.str(), kTextAlignLeft);
ypos += myLineHeight + 8;
xpos += 40;
myROMBank =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($3E) "),
myLineHeight, romitems, "ROM ($3F): ",
_font.getStringWidth("ROM ($3F): "), kROMBankChanged);
myROMBank->setTarget(this);
addFocusWidget(myROMBank);
xpos += myROMBank->getWidth() + 20;
myRAMBank =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($3E) "),
myLineHeight, ramitems, "RAM ($3E): ",
_font.getStringWidth("RAM ($3E): "), kRAMBankChanged);
myRAMBank->setTarget(this);
addFocusWidget(myRAMBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EWidget::loadConfig()
{
if(myCart.myCurrentBank < 256)
{
myROMBank->setSelectedIndex(myCart.myCurrentBank % myNumRomBanks);
myRAMBank->setSelectedMax();
}
else
{
myROMBank->setSelectedMax();
myRAMBank->setSelectedIndex(myCart.myCurrentBank - 256);
}
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EWidget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
int bank = -1;
if(cmd == kROMBankChanged)
{
if(myROMBank->getSelected() < (int)myNumRomBanks)
{
bank = myROMBank->getSelected();
myRAMBank->setSelectedMax();
}
else
{
bank = 256; // default to first RAM bank
myRAMBank->setSelectedIndex(0);
}
}
else if(cmd == kRAMBankChanged)
{
if(myRAMBank->getSelected() < (int)myNumRamBanks)
{
myROMBank->setSelectedMax();
bank = myRAMBank->getSelected() + 256;
}
else
{
bank = 0; // default to first ROM bank
myROMBank->setSelectedIndex(0);
}
}
myCart.unlockBank();
myCart.bank(bank);
myCart.lockBank();
invalidate();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge3EWidget::bankState()
{
ostringstream& buf = buffer();
uInt16& bank = myCart.myCurrentBank;
if(bank < 256)
buf << "ROM bank " << dec << bank % myNumRomBanks << ", RAM inactive";
else
buf << "ROM inactive, RAM bank " << bank % myNumRomBanks;
return buf.str();
}
+54
View File
@@ -0,0 +1,54 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart3EWidget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CARTRIDGE3E_WIDGET_HXX
#define CARTRIDGE3E_WIDGET_HXX
class Cartridge3E;
class PopUpWidget;
#include "CartDebugWidget.hxx"
class Cartridge3EWidget : public CartDebugWidget
{
public:
Cartridge3EWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
Cartridge3E& cart);
virtual ~Cartridge3EWidget() { }
void loadConfig();
void handleCommand(CommandSender* sender, int cmd, int data, int id);
string bankState();
private:
Cartridge3E& myCart;
const uInt32 myNumRomBanks;
const uInt32 myNumRamBanks;
PopUpWidget *myROMBank, *myRAMBank;
enum {
kROMBankChanged = 'rmCH',
kRAMBankChanged = 'raCH'
};
};
#endif
+91
View File
@@ -0,0 +1,91 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart3FWidget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Cart3F.hxx"
#include "PopUpWidget.hxx"
#include "Cart3FWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge3FWidget::Cartridge3FWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge3F& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{
uInt32 size = cart.mySize;
ostringstream info;
info << "Tigervision 3F cartridge, 2-256 2K banks\n"
<< "Startup bank = " << cart.myStartBank << "\n"
<< "First 2K bank selected by writing to $3F\n"
<< "Last 2K always points to last 2K of ROM\n";
// Eventually, we should query this from the debugger/disassembler
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
start -= start % 0x1000;
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
int xpos = 10,
ypos = addBaseInformation(size, "TigerVision", info.str()) + myLineHeight;
VariantList items;
for(uInt16 i = 0; i < cart.bankCount(); ++i)
items.push_back(Variant(i).toString() + " ($3F)");
ostringstream label;
label << "Set bank ($" << Common::Base::HEX4 << start << " - $" <<
(start+0x7FF) << "): ";
myBank =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($3F) "),
myLineHeight, items, label.str(),
_font.getStringWidth(label.str()), kBankChanged);
myBank->setTarget(this);
addFocusWidget(myBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3FWidget::loadConfig()
{
myBank->setSelectedIndex(myCart.myCurrentBank);
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3FWidget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
if(cmd == kBankChanged)
{
myCart.unlockBank();
myCart.bank(myBank->getSelected());
myCart.lockBank();
invalidate();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge3FWidget::bankState()
{
ostringstream& buf = buffer();
buf << "Bank = " << dec << myCart.myCurrentBank << ", hotspot = $3F";
return buf.str();
}
+49
View File
@@ -0,0 +1,49 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart3FWidget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CARTRIDGE3F_WIDGET_HXX
#define CARTRIDGE3F_WIDGET_HXX
class Cartridge3F;
class PopUpWidget;
#include "CartDebugWidget.hxx"
class Cartridge3FWidget : public CartDebugWidget
{
public:
Cartridge3FWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
Cartridge3F& cart);
virtual ~Cartridge3FWidget() { }
void loadConfig();
void handleCommand(CommandSender* sender, int cmd, int data, int id);
string bankState();
private:
Cartridge3F& myCart;
PopUpWidget* myBank;
enum { kBankChanged = 'bkCH' };
};
#endif
+287
View File
@@ -0,0 +1,287 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart4A50Widget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Cart4A50.hxx"
#include "PopUpWidget.hxx"
#include "Cart4A50Widget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge4A50Widget::Cartridge4A50Widget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge4A50& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{
string info =
"4A50 cartridge - 128K ROM and 32K RAM, split in various bank configurations\n"
"Multiple hotspots, see documentation for further details\n"
"Lower bank region (2K) : $F000 - $F7FF\n"
"Middle bank region (1.5K): $F800 - $FDFF\n"
"High bank region (256B) : $FE00 - $FEFF\n"
"Fixed (last 256B of ROM) : $FF00 - $FFFF\n";
int xpos = 10,
ypos = addBaseInformation(cart.mySize, "John Payson / Supercat", info) +
myLineHeight;
VariantList items16, items32, items128, items256;
for(uInt32 i = 0; i < 16; ++i)
items16.push_back(i);
items16.push_back("Inactive", "");
for(uInt32 i = 0; i < 32; ++i)
items32.push_back(i);
items32.push_back("Inactive", "");
for(uInt32 i = 0; i < 128; ++i)
items128.push_back(i);
items128.push_back("Inactive", "");
for(uInt32 i = 0; i < 256; ++i)
items256.push_back(i);
items256.push_back("Inactive", "");
string lowerlabel = "Set lower 2K region ($F000 - $F7FF): ";
string middlelabel = "Set middle 1.5K region ($F800 - $FDFF): ";
string highlabel = "Set high 256B region ($FE00 - $FEFF): ";
const int lwidth = _font.getStringWidth(middlelabel),
fwidth = _font.getStringWidth("Inactive"),
flwidth = _font.getStringWidth("ROM: ");
// Lower bank/region configuration
xpos = 10;
new StaticTextWidget(_boss, _font, xpos, ypos, lwidth,
myFontHeight, lowerlabel, kTextAlignLeft);
ypos += myLineHeight + 8;
xpos += 40;
myROMLower =
new PopUpWidget(boss, _font, xpos, ypos-2, fwidth, myLineHeight,
items32, "ROM: ", flwidth, kROMLowerChanged);
myROMLower->setTarget(this);
addFocusWidget(myROMLower);
xpos += myROMLower->getWidth() + 20;
myRAMLower =
new PopUpWidget(boss, _font, xpos, ypos-2, fwidth, myLineHeight,
items16, "RAM: ", flwidth, kRAMLowerChanged);
myRAMLower->setTarget(this);
addFocusWidget(myRAMLower);
// Middle bank/region configuration
xpos = 10; ypos += myLineHeight + 14;
new StaticTextWidget(_boss, _font, xpos, ypos, lwidth,
myFontHeight, middlelabel, kTextAlignLeft);
ypos += myLineHeight + 8;
xpos += 40;
myROMMiddle =
new PopUpWidget(boss, _font, xpos, ypos-2, fwidth, myLineHeight,
items32, "ROM: ", flwidth, kROMMiddleChanged);
myROMMiddle->setTarget(this);
addFocusWidget(myROMMiddle);
xpos += myROMMiddle->getWidth() + 20;
myRAMMiddle =
new PopUpWidget(boss, _font, xpos, ypos-2, fwidth, myLineHeight,
items16, "RAM: ", flwidth, kRAMMiddleChanged);
myRAMMiddle->setTarget(this);
addFocusWidget(myRAMMiddle);
// High bank/region configuration
xpos = 10; ypos += myLineHeight + 14;
new StaticTextWidget(_boss, _font, xpos, ypos, lwidth,
myFontHeight, highlabel, kTextAlignLeft);
ypos += myLineHeight + 8;
xpos += 40;
myROMHigh =
new PopUpWidget(boss, _font, xpos, ypos-2, fwidth, myLineHeight,
items256, "ROM: ", flwidth, kROMHighChanged);
myROMHigh->setTarget(this);
addFocusWidget(myROMHigh);
xpos += myROMHigh->getWidth() + 20;
myRAMHigh =
new PopUpWidget(boss, _font, xpos, ypos-2, fwidth, myLineHeight,
items128, "RAM: ", flwidth, kRAMHighChanged);
myRAMHigh->setTarget(this);
addFocusWidget(myRAMHigh);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge4A50Widget::loadConfig()
{
// Lower bank
if(myCart.myIsRomLow) // ROM active
{
myROMLower->setSelectedIndex((myCart.mySliceLow >> 11) & 0x1F);
myRAMLower->setSelectedMax();
}
else // RAM active
{
myROMLower->setSelectedMax();
myRAMLower->setSelectedIndex((myCart.mySliceLow >> 11) & 0x0F);
}
// Middle bank
if(myCart.myIsRomMiddle) // ROM active
{
myROMMiddle->setSelectedIndex((myCart.mySliceMiddle >> 11) & 0x1F);
myRAMMiddle->setSelectedMax();
}
else // RAM active
{
myROMMiddle->setSelectedMax();
myRAMMiddle->setSelectedIndex((myCart.mySliceMiddle >> 11) & 0x0F);
}
// High bank
if(myCart.myIsRomHigh) // ROM active
{
myROMHigh->setSelectedIndex((myCart.mySliceHigh >> 11) & 0xFF);
myRAMHigh->setSelectedMax();
}
else // RAM active
{
myROMHigh->setSelectedMax();
myRAMHigh->setSelectedIndex((myCart.mySliceHigh >> 11) & 0x7F);
}
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge4A50Widget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
myCart.unlockBank();
switch(cmd)
{
case kROMLowerChanged:
if(myROMLower->getSelected() < 32)
{
myCart.bankROMLower(myROMLower->getSelected());
myRAMLower->setSelectedMax();
}
else
{
// default to first RAM bank
myRAMLower->setSelectedIndex(0);
myCart.bankRAMLower(0);
}
break;
case kRAMLowerChanged:
if(myRAMLower->getSelected() < 16)
{
myROMLower->setSelectedMax();
myCart.bankRAMLower(myRAMLower->getSelected());
}
else
{
// default to first ROM bank
myROMLower->setSelectedIndex(0);
myCart.bankROMLower(0);
}
break;
case kROMMiddleChanged:
if(myROMMiddle->getSelected() < 32)
{
myCart.bankROMMiddle(myROMMiddle->getSelected());
myRAMMiddle->setSelectedMax();
}
else
{
// default to first RAM bank
myRAMMiddle->setSelectedIndex(0);
myCart.bankRAMMiddle(0);
}
break;
case kRAMMiddleChanged:
if(myRAMMiddle->getSelected() < 16)
{
myROMMiddle->setSelectedMax();
myCart.bankRAMMiddle(myRAMMiddle->getSelected());
}
else
{
// default to first ROM bank
myROMMiddle->setSelectedIndex(0);
myCart.bankROMMiddle(0);
}
break;
case kROMHighChanged:
if(myROMHigh->getSelected() < 256)
{
myCart.bankROMHigh(myROMHigh->getSelected());
myRAMHigh->setSelectedMax();
}
else
{
// default to first RAM bank
myRAMHigh->setSelectedIndex(0);
myCart.bankRAMHigh(0);
}
break;
case kRAMHighChanged:
if(myRAMHigh->getSelected() < 128)
{
myROMHigh->setSelectedMax();
myCart.bankRAMHigh(myRAMHigh->getSelected());
}
else
{
// default to first ROM bank
myROMHigh->setSelectedIndex(0);
myCart.bankROMHigh(0);
}
break;
}
myCart.lockBank();
invalidate();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge4A50Widget::bankState()
{
ostringstream& buf = buffer();
buf << "L/M/H = " << dec;
if(myCart.myIsRomLow)
buf << "ROM bank " << ((myCart.mySliceLow >> 11) & 0x1F) << " / ";
else
buf << "RAM bank " << ((myCart.mySliceLow >> 11) & 0x0F) << " / ";
if(myCart.myIsRomMiddle)
buf << "ROM bank " << ((myCart.mySliceMiddle >> 11) & 0x1F) << " / ";
else
buf << "RAM bank " << ((myCart.mySliceMiddle >> 11) & 0x0F) << " / ";
if(myCart.myIsRomHigh)
buf << "ROM bank " << ((myCart.mySliceHigh >> 11) & 0xFF);
else
buf << "RAM bank " << ((myCart.mySliceHigh >> 11) & 0x7F);
return buf.str();
}
+58
View File
@@ -0,0 +1,58 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart4A50Widget.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#ifndef CARTRIDGE4A50_WIDGET_HXX
#define CARTRIDGE4A50_WIDGET_HXX
class Cartridge4A50;
class PopUpWidget;
#include "CartDebugWidget.hxx"
class Cartridge4A50Widget : public CartDebugWidget
{
public:
Cartridge4A50Widget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
Cartridge4A50& cart);
virtual ~Cartridge4A50Widget() { }
void loadConfig();
void handleCommand(CommandSender* sender, int cmd, int data, int id);
string bankState();
private:
Cartridge4A50& myCart;
PopUpWidget *myROMLower, *myRAMLower;
PopUpWidget *myROMMiddle, *myRAMMiddle;
PopUpWidget *myROMHigh, *myRAMHigh;
enum {
kROMLowerChanged = 'rmLW',
kRAMLowerChanged = 'raLW',
kROMMiddleChanged = 'rmMD',
kRAMMiddleChanged = 'raMD',
kROMHighChanged = 'rmHI',
kRAMHighChanged = 'raHI'
};
};
#endif
+41
View File
@@ -0,0 +1,41 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart4KSCWidget.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================
#include "Cart4KSC.hxx"
#include "Cart4KSCWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge4KSCWidget::Cartridge4KSCWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge4KSC& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h)
{
// Eventually, we should query this from the debugger/disassembler
uInt16 start = (cart.myImage[0xFFD] << 8) | cart.myImage[0xFFC];
start -= start % 0x1000;
ostringstream info;
info << "4KSC cartridge, non-bankswitched\n"
<< "128 bytes RAM @ $F000 - $F0FF\n"
<< " $F080 - $F0FF (R), $F000 - $F07F (W)\n"
<< "Accessible @ $" << Common::Base::HEX4 << start << " - "
<< "$" << (start + 0xFFF);
addBaseInformation(4096, "homebrew intermediate format", info.str());
}

Some files were not shown because too many files have changed in this diff Show More