Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d8636904c | |||
| f0b732c227 | |||
| 819d8ff77c | |||
| 925d78700b | |||
| fa72a9f10b | |||
| 8474ecf2c3 | |||
| bd88d61225 | |||
| 8bb34a7344 | |||
| ff0473d70d | |||
| fcd9a2b709 | |||
| 06dade8f65 | |||
| af845d3c41 | |||
| 946332d472 | |||
| bd8ce9e153 | |||
| 01f037446b | |||
| cbec084d68 | |||
| 551c15eba5 | |||
| b62c66577e | |||
| 03c673848e | |||
| 1c002610fd | |||
| 8b1fd4728e | |||
| 6de171184b | |||
| 0058de535e | |||
| 44a3e1c7d3 | |||
| 63561f1d8d | |||
| 16bd173e3b | |||
| 4c68b2fd96 | |||
| 9a53f0d247 | |||
| 7d65671023 | |||
| 4e2330e6c5 | |||
| 594f5e604c | |||
| a6829f3817 | |||
| e4d3ae497d | |||
| e07710ae7b | |||
| 762ddaf8cf |
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
//}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 284 B |
@@ -0,0 +1,38 @@
|
||||
/* XPM */
|
||||
static const char * stella_icon[] = {
|
||||
"32 32 3 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"+ c #FFFFFF",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" .............. ",
|
||||
" .............. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ..++..++..++.. ",
|
||||
" ....++..++..++.... ",
|
||||
" ....++..++..++.... ",
|
||||
" ....++++..++..++++.... ",
|
||||
" ....++++..++..++++.... ",
|
||||
" ..++++....++....++++.. ",
|
||||
" ..++++....++....++++.. ",
|
||||
" ......++......++......++...... ",
|
||||
" ......++......++......++...... ",
|
||||
" ..++++++.. ..++.. ..++++++.. ",
|
||||
" ..++++++.. ..++.. ..++++++.. ",
|
||||
" ..++++.... ..++.. ....++++.. ",
|
||||
" ..++++.... ..++.. ....++++.. ",
|
||||
" ........ ...... ........ ",
|
||||
" ........ ...... ........ ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" "};
|
||||
@@ -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 }
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||