Merge branch 'master' into feature-highscores
|
@ -31,3 +31,5 @@ src/**/*.psess
|
||||||
src/**/*.vspx
|
src/**/*.vspx
|
||||||
src/**/**.pdb
|
src/**/**.pdb
|
||||||
Stella.xcscheme
|
Stella.xcscheme
|
||||||
|
src/tools/fonts/*
|
||||||
|
|
||||||
|
|
22
Announce.txt
|
@ -9,7 +9,7 @@
|
||||||
SSSS ttt eeeee llll llll aaaaa
|
SSSS ttt eeeee llll llll aaaaa
|
||||||
|
|
||||||
===========================================================================
|
===========================================================================
|
||||||
Release 6.1 for Linux, macOS and Windows
|
Release 6.2.1 for Linux, macOS and Windows
|
||||||
===========================================================================
|
===========================================================================
|
||||||
|
|
||||||
The Atari 2600 Video Computer System (VCS), introduced in 1977, was the
|
The Atari 2600 Video Computer System (VCS), introduced in 1977, was the
|
||||||
|
@ -21,27 +21,27 @@ all of your favourite Atari 2600 games again! Stella was originally
|
||||||
developed for Linux by Bradford W. Mott, however, it has been ported to a
|
developed for Linux by Bradford W. Mott, however, it has been ported to a
|
||||||
number of other platforms and is currently maintained by Stephen Anthony.
|
number of other platforms and is currently maintained by Stephen Anthony.
|
||||||
|
|
||||||
This is the 6.1 release of Stella for Linux, macOS and Windows. The
|
This is the 6.2.1 release of Stella for Linux, macOS and Windows. The
|
||||||
distributions currently available are:
|
distributions currently available are:
|
||||||
|
|
||||||
* Binaries for Windows Vista/7/8/10 :
|
* Binaries for Windows Vista/7/8/10 :
|
||||||
Stella-6.1-win32.exe (32-bit EXE installer)
|
Stella-6.2.1-win32.exe (32-bit EXE installer)
|
||||||
Stella-6.1-x64.exe (64-bit EXE installer)
|
Stella-6.2.1-x64.exe (64-bit EXE installer)
|
||||||
Stella-6.1-windows.zip (32/64 bit versions)
|
Stella-6.2.1-windows.zip (32/64 bit versions)
|
||||||
|
|
||||||
* Binary distribution for macOS 10.7 and above :
|
* Binary distribution for macOS 10.7 and above :
|
||||||
Stella-6.1-macos.dmg (64-bit Intel)
|
Stella-6.2.1-macos.dmg (64-bit Intel)
|
||||||
|
|
||||||
* Binary distribution in 32-bit & 64-bit Ubuntu DEB format :
|
* Binary distribution in 32-bit & 64-bit Ubuntu DEB format :
|
||||||
stella_6.1-1_i386.deb
|
stella_6.2.1-1_i386.deb
|
||||||
stella_6.1-1_amd64.deb
|
stella_6.2.1-1_amd64.deb
|
||||||
|
|
||||||
* Binary distribution in 32-bit & 64-bit RPM format :
|
* Binary distribution in 32-bit & 64-bit RPM format :
|
||||||
stella-6.1-2.i386.rpm
|
stella-6.2.1-2.i386.rpm
|
||||||
stella-6.1-2.x86_64.rpm
|
stella-6.2.1-2.x86_64.rpm
|
||||||
|
|
||||||
* Source code distribution for all platforms :
|
* Source code distribution for all platforms :
|
||||||
stella-6.1-src.tar.xz
|
stella-6.2.1-src.tar.xz
|
||||||
|
|
||||||
|
|
||||||
Distribution Site
|
Distribution Site
|
||||||
|
|
161
Changes.txt
|
@ -12,7 +12,155 @@
|
||||||
Release History
|
Release History
|
||||||
===========================================================================
|
===========================================================================
|
||||||
|
|
||||||
6.0.2 to 6.1: (MM dd, 2020)
|
6.2.1 to 6.3 (XXXX XX, 2020)
|
||||||
|
|
||||||
|
* Added adjustable autofire.
|
||||||
|
|
||||||
|
* Added 'Dark' UI theme. (TODO: DOC)
|
||||||
|
|
||||||
|
* Extended global hotkeys for debug options.
|
||||||
|
|
||||||
|
* Added option to playback a game using the Time Machine.
|
||||||
|
|
||||||
|
* Allow taking snapshots from within the Time Machine dialog.
|
||||||
|
|
||||||
|
* Added the ability to access most files that Stella uses from within a
|
||||||
|
ZIP file. This includes the following:
|
||||||
|
- Per-ROM properties file (so one can distribute a ROM and its
|
||||||
|
associated properties).
|
||||||
|
- Debugger symbol (.sym) and list (.lst) files, etc.
|
||||||
|
- Several others, as we extend the support.
|
||||||
|
Basically, you are now able to put many files that Stella uses inside
|
||||||
|
one ZIP file, and distribute just that file.
|
||||||
|
|
||||||
|
* Added option to select the audio device.
|
||||||
|
|
||||||
|
* Added option to display detected settings info when a ROM is loaded.
|
||||||
|
|
||||||
|
* Replaced "Re-disassemble" with "Disassemble @ current line" in debugger.
|
||||||
|
|
||||||
|
* Fixed bug when taking fullscreen snapshots; the dimensions were
|
||||||
|
sometimes cut off.
|
||||||
|
|
||||||
|
-Have fun!
|
||||||
|
|
||||||
|
|
||||||
|
6.2 to 6.2.1: (June 20, 2020)
|
||||||
|
|
||||||
|
* Fixed Pitfall II ROM not working correctly.
|
||||||
|
|
||||||
|
* Fixed crashes when using some combinations of bankswitching schemes on
|
||||||
|
incorrect ROMs, or when using invalid ROM file sizes, etc.
|
||||||
|
|
||||||
|
* Fixed RIOT timer behaviour on reading/writing at the wraparound cycle.
|
||||||
|
|
||||||
|
* Fixed incorrectly setting D6 bit on TIA reads in some cases. Related
|
||||||
|
to this, improve 'tiadriven' option to randomize only D5..D0 bits.
|
||||||
|
|
||||||
|
* Fixed custom palette and TV effects adjustable slider rounding issue.
|
||||||
|
|
||||||
|
* Fixed some bugs in 3E+ scheme when using non-standard ROM sizes.
|
||||||
|
|
||||||
|
* Fixed crash in Audio & Video dialog when opened from debugger, and the
|
||||||
|
debugger window sometimes being resized when using the Options dialog.
|
||||||
|
|
||||||
|
* Make NTSC custom phase shift not affect Yellow anymore.
|
||||||
|
|
||||||
|
* Fixed '1x' snapshot mode; TV effects are now disabled. This mode
|
||||||
|
now generates a clean, pixel-exact image.
|
||||||
|
|
||||||
|
* Fixed mappings sometimes not being saved in the Retron77 port.
|
||||||
|
|
||||||
|
* A ROM properties file may now be placed next to the ROM (with the same
|
||||||
|
name as the ROM, except ending in .pro), and Stella will automatically
|
||||||
|
apply the properties to the ROM. [NOTE: this was present in 6.2, but
|
||||||
|
was mistakenly left out of the changelog]
|
||||||
|
|
||||||
|
* Added button to Game Info dialog to save properties of the currently
|
||||||
|
loaded ROM to a separate properties file (in the default save directory).
|
||||||
|
This is useful in conjunction with the previous item.
|
||||||
|
|
||||||
|
* Allow changing custom palette and TV effects adjustables in 1% steps
|
||||||
|
again.
|
||||||
|
|
||||||
|
* Updated documentation for changes in ROM properties key names.
|
||||||
|
|
||||||
|
* The codebase now compiles under gcc6 again. Future versions will
|
||||||
|
require gcc7, though.
|
||||||
|
|
||||||
|
|
||||||
|
6.1.2 to 6.2: (June 7, 2020)
|
||||||
|
|
||||||
|
* Added interactive palette to Video & Audio settings.
|
||||||
|
|
||||||
|
* Added 'Custom' palette, generated from user controlled phase shifts.
|
||||||
|
|
||||||
|
* Added that adjustable audio & video settings are displayed as gauge bars.
|
||||||
|
|
||||||
|
* Added four global hotkeys which allow selecting and changing numerous
|
||||||
|
audio & video settings without having to remember the dedicated hotkeys.
|
||||||
|
|
||||||
|
* Added 'Turbo' mode, runs the game as fast as the computer allows.
|
||||||
|
|
||||||
|
* Added that paddle centering (per ROM) and sensitivity can be adjusted.
|
||||||
|
|
||||||
|
* Added that mouse sensitivity for Driving controller can be adjusted.
|
||||||
|
|
||||||
|
* Added paddle filtering in UI to avoid unwanted navigation events.
|
||||||
|
|
||||||
|
* Added selectable dialog fonts.
|
||||||
|
|
||||||
|
* Added separate positioning of launcher, emulator and debugger.
|
||||||
|
|
||||||
|
* Added optional display to game refresh rate adaption in fullscreen mode.
|
||||||
|
|
||||||
|
* Added option which lets default ROM path follow launcher navigation.
|
||||||
|
|
||||||
|
* Added debugger 'saveaccess' function, which saves memory access counts to
|
||||||
|
a CSV file.
|
||||||
|
|
||||||
|
* Added displaying last write address in the debugger.
|
||||||
|
|
||||||
|
* Added debugger pseudo-register '_scanend', which gives the number of
|
||||||
|
scanlines at the end of the last frame.
|
||||||
|
|
||||||
|
* Added detection of color and audio data in DiStella.
|
||||||
|
|
||||||
|
* Restored 'cfg' directory for Distella config files.
|
||||||
|
|
||||||
|
* Added TV Boy and 3EX bank switching types.
|
||||||
|
|
||||||
|
* Removed unused CV+ and DASH bank switching types.
|
||||||
|
|
||||||
|
* Added support for loading grayscale PNG images in the ROM launcher.
|
||||||
|
|
||||||
|
|
||||||
|
6.1.1 to 6.1.2: (April 25, 2020)
|
||||||
|
|
||||||
|
* Fixed bug with remapped events not being reloaded in certain cases.
|
||||||
|
|
||||||
|
* Fixed bug in debugger for 3E scheme when displaying active RAM bank.
|
||||||
|
|
||||||
|
* Fixed bug in "Dragon Defender" ROM being misconfigured for Mindlink
|
||||||
|
controller.
|
||||||
|
|
||||||
|
|
||||||
|
6.1 to 6.1.1: (April 4, 2020)
|
||||||
|
|
||||||
|
* Fixed crash in 3E bankswitching scheme when writing to ROM addresses.
|
||||||
|
|
||||||
|
* Fix snapshots on Retina HiDPI displays capturing only the top-left
|
||||||
|
corner.
|
||||||
|
|
||||||
|
* Fixed wrong color for BK (background) swatch in the debugger.
|
||||||
|
|
||||||
|
* Fixed 'Right Diff' button in Command menu changing left difficulty
|
||||||
|
instead.
|
||||||
|
|
||||||
|
* Fixed compilation of libretro port on Debian Buster.
|
||||||
|
|
||||||
|
|
||||||
|
6.0.2 to 6.1: (March 22, 2020)
|
||||||
|
|
||||||
* IMPORTANT NOTES:
|
* IMPORTANT NOTES:
|
||||||
- Because of major event remapping changes, all remappings will be reset
|
- Because of major event remapping changes, all remappings will be reset
|
||||||
|
@ -119,7 +267,9 @@
|
||||||
|
|
||||||
* Added option to change pitch of Pitfall II music.
|
* Added option to change pitch of Pitfall II music.
|
||||||
|
|
||||||
* ROM Info Launcher can now display multiple lines per property and
|
* ROM Info Viewer size is not limited to fixed zoom steps anymore.
|
||||||
|
|
||||||
|
* ROM Info Viewer can now display multiple lines per property and the
|
||||||
bank switching type.
|
bank switching type.
|
||||||
|
|
||||||
* In file listings, you can now select directories by holding 'Shift' on
|
* In file listings, you can now select directories by holding 'Shift' on
|
||||||
|
@ -179,6 +329,10 @@
|
||||||
* Fixed bug in DPC+ scheme; 'fast fetch mode' was enabled at startup,
|
* Fixed bug in DPC+ scheme; 'fast fetch mode' was enabled at startup,
|
||||||
when it should be disabled by default.
|
when it should be disabled by default.
|
||||||
|
|
||||||
|
* Some more work on DPC+ playfield 'jitter' effect for certain older DPC+
|
||||||
|
driver versions; more ROMs are now detected properly. Special thanks
|
||||||
|
to SpiceWare for his research in this area.
|
||||||
|
|
||||||
* Added proper Retron77 port.
|
* Added proper Retron77 port.
|
||||||
|
|
||||||
* Added proper libretro port, and fixed display for OpenGLES renderers.
|
* Added proper libretro port, and fixed display for OpenGLES renderers.
|
||||||
|
@ -192,7 +346,8 @@
|
||||||
|
|
||||||
* Updated included PNG library to latest stable version.
|
* Updated included PNG library to latest stable version.
|
||||||
|
|
||||||
-Have fun!
|
* Updated UNIX configure script to work with the gcc version 10 and
|
||||||
|
above.
|
||||||
|
|
||||||
|
|
||||||
6.0.1 to 6.0.2: (October 11, 2019)
|
6.0.1 to 6.0.2: (October 11, 2019)
|
||||||
|
|
7
Makefile
|
@ -8,14 +8,11 @@
|
||||||
## SS SS tt ee ll ll aa aa
|
## SS SS tt ee ll ll aa aa
|
||||||
## SSSS ttt eeeee llll llll aaaaa
|
## SSSS ttt eeeee llll llll aaaaa
|
||||||
##
|
##
|
||||||
## Copyright (c) 1995-2016 by Bradford W. Mott, Stephen Anthony
|
## Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
|
||||||
## and the Stella Team
|
## and the Stella Team
|
||||||
##
|
##
|
||||||
## See the file "License.txt" for information on usage and redistribution of
|
## See the file "License.txt" for information on usage and redistribution of
|
||||||
## this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
## this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
##
|
|
||||||
## Based on code from ScummVM - Scumm Interpreter
|
|
||||||
## Copyright (C) 2002-2004 The ScummVM project
|
|
||||||
##============================================================================
|
##============================================================================
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -99,7 +96,7 @@ EXECUTABLE := stella$(EXEEXT)
|
||||||
EXECUTABLE_PROFILE_GENERATE := stella-pgo-generate$(EXEEXT)
|
EXECUTABLE_PROFILE_GENERATE := stella-pgo-generate$(EXEEXT)
|
||||||
EXECUTABLE_PROFILE_USE := stella-pgo$(EXEEXT)
|
EXECUTABLE_PROFILE_USE := stella-pgo$(EXEEXT)
|
||||||
|
|
||||||
PROFILE_DIR = $(CURDIR)/profile
|
PROFILE_DIR = $(CURDIR)/test/roms/profile
|
||||||
PROFILE_OUT = $(PROFILE_DIR)/out
|
PROFILE_OUT = $(PROFILE_DIR)/out
|
||||||
PROFILE_STAMP = profile.stamp
|
PROFILE_STAMP = profile.stamp
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,12 @@ environment:
|
||||||
|
|
||||||
Configuration: Release
|
Configuration: Release
|
||||||
|
|
||||||
SDL2_version: 2.0.10
|
SDL2_version: 2.0.12
|
||||||
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- cmd: |
|
- cmd: |
|
||||||
curl -o "C:\SDL2-devel.zip" https://www.libsdl.org/release/SDL2-devel-2.0.10-VC.zip
|
curl -o "C:\SDL2-devel.zip" "https://www.libsdl.org/release/SDL2-devel-%SDL2_version%-VC.zip"
|
||||||
7z x "C:\SDL2-devel.zip" -o"C:\"
|
7z x "C:\SDL2-devel.zip" -o"C:\"
|
||||||
xcopy /S "C:\SDL2-%SDL2_version%\include" src\common
|
xcopy /S "C:\SDL2-%SDL2_version%\include" src\common
|
||||||
|
|
||||||
|
|
|
@ -458,11 +458,17 @@ elif test "$have_gcc" = yes; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case $cxx_version in
|
case $cxx_version in
|
||||||
4.[7-9]|4.[7-9].[0-9]|4.[7-9].[0-9][-.]*|[5-9]|[5-9].[0-9]|[5-9].[0-9].[0-9]|[5-9].[0-9].[0-9][-.]*)
|
[1-9]*)
|
||||||
_cxx_major=`echo $cxx_version | cut -d '.' -f 1`
|
_cxx_major=`echo $cxx_version | cut -d '.' -f 1`
|
||||||
_cxx_minor=`echo $cxx_version | cut -d '.' -f 2`
|
_cxx_minor=`echo $cxx_version | cut -d '.' -f 2`
|
||||||
|
# Need at least version 4.7
|
||||||
|
if [ $_cxx_major -ge 5 ] || [ $_cxx_major -eq 4 -a $_cxx_minor -ge 7 ]; then
|
||||||
cxx_version="$cxx_version, ok"
|
cxx_version="$cxx_version, ok"
|
||||||
cxx_verc_fail=no
|
cxx_verc_fail=no
|
||||||
|
else
|
||||||
|
cxx_version="$cxx_version, bad"
|
||||||
|
cxx_verc_fail=yes
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
'not found')
|
'not found')
|
||||||
cxx_verc_fail=yes
|
cxx_verc_fail=yes
|
||||||
|
|
|
@ -1,3 +1,38 @@
|
||||||
|
stella (6.2.1-1) stable; urgency=high
|
||||||
|
|
||||||
|
* Version 6.2.1 release
|
||||||
|
|
||||||
|
-- Stephen Anthony <sa666666@gmail.com> Sat, 20 Jun 2020 17:09:59 -0230
|
||||||
|
|
||||||
|
|
||||||
|
stella (6.2-1) stable; urgency=high
|
||||||
|
|
||||||
|
* Version 6.2 release
|
||||||
|
|
||||||
|
-- Stephen Anthony <sa666666@gmail.com> Sun, 7 Jun 2020 17:09:59 -0230
|
||||||
|
|
||||||
|
|
||||||
|
stella (6.1.2-1) stable; urgency=high
|
||||||
|
|
||||||
|
* Version 6.1.2 release
|
||||||
|
|
||||||
|
-- Stephen Anthony <sa666666@gmail.com> Sat, 25 Apr 2020 17:09:59 -0230
|
||||||
|
|
||||||
|
|
||||||
|
stella (6.1.1-1) stable; urgency=high
|
||||||
|
|
||||||
|
* Version 6.1.1 release
|
||||||
|
|
||||||
|
-- Stephen Anthony <sa666666@gmail.com> Sat, 04 Apr 2020 17:09:59 -0230
|
||||||
|
|
||||||
|
|
||||||
|
stella (6.1-1) stable; urgency=high
|
||||||
|
|
||||||
|
* Version 6.1 release
|
||||||
|
|
||||||
|
-- Stephen Anthony <sa666666@gmail.com> Sun, 22 Mar 2020 17:09:59 -0230
|
||||||
|
|
||||||
|
|
||||||
stella (6.0.2-1) stable; urgency=high
|
stella (6.0.2-1) stable; urgency=high
|
||||||
|
|
||||||
* Version 6.0.2 release
|
* Version 6.0.2 release
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<center><b><font size="7">Stella</font></b></center>
|
<center><b><font size="7">Stella</font></b></center>
|
||||||
<center><h4><b>Release 6.1</b></h4></center>
|
<center><h4><b>Release 6.2.1</b></h4></center>
|
||||||
<center><h1><b>Integrated Debugger</b></h1></center>
|
<center><h1><b>Integrated Debugger</b></h1></center>
|
||||||
<center><h4><b>(a work in progress)</b></h4></center>
|
<center><h4><b>(a work in progress)</b></h4></center>
|
||||||
<br>
|
<br>
|
||||||
|
@ -121,13 +121,14 @@ feature that no other 2600 debugger has; it's <b>completely</b> cross-platform.<
|
||||||
|
|
||||||
<li>Supports Distella 'configuration directives', which may be used to
|
<li>Supports Distella 'configuration directives', which may be used to
|
||||||
override automatic code/data determination in the disassembly. For now,
|
override automatic code/data determination in the disassembly. For now,
|
||||||
the following directives are supported: CODE, GFX, PGFX, DATA, ROW.
|
the following directives are supported: CODE, GFX, PGFX, COL, PCOL, BCOL, AUD, DATA, ROW.
|
||||||
These directives can be entered at the debugger prompt, or (automatically)
|
These directives can be entered at the debugger prompt, or be (automatically)
|
||||||
loaded and saved in configuration files.</li>
|
loaded and saved in configuration files.</li>
|
||||||
|
|
||||||
<li>Extensive disassembly support, both from the emulation core and with help
|
<li>Extensive disassembly support, both from the emulation core and with help
|
||||||
from Distella. Where possible, the disassembly differentiates between code,
|
from Distella. Where possible, the disassembly differentiates between code,
|
||||||
player graphics and playfield graphics (ie, addresses stored in GRPx and PFx)
|
player and playfield graphics, colors and audio (ie, addresses stored in
|
||||||
|
GRPx, PFx, COLUxx, AUDxx)
|
||||||
and data (addresses used as an operand of a command). Code sections are also
|
and data (addresses used as an operand of a command). Code sections are also
|
||||||
differentiated between actual code, and 'tentative' code (ie, areas that may
|
differentiated between actual code, and 'tentative' code (ie, areas that may
|
||||||
represent code sections, but haven't actually been executed yet). Such
|
represent code sections, but haven't actually been executed yet). Such
|
||||||
|
@ -330,7 +331,7 @@ previous rewind operation. The rewind buffer is 100 levels deep by default, the
|
||||||
size can be configured e.g. in the
|
size can be configured e.g. in the
|
||||||
<b><a href="index.html#Debugger">Developer Settings</a> - Time Machine</b> dialog.<p>
|
<b><a href="index.html#Debugger">Developer Settings</a> - Time Machine</b> dialog.<p>
|
||||||
|
|
||||||
<p>The other operations are Step, Trace, Scan+1, Frame+1 and Exit (debugger).</p>
|
<p>The other operations are Step, Trace, Scan+1, Frame+1 and Run.</p>
|
||||||
|
|
||||||
<p>You can also use the buttons from anywhere in the GUI via hotkeys.</p>
|
<p>You can also use the buttons from anywhere in the GUI via hotkeys.</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -381,12 +382,12 @@ size can be configured e.g. in the
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Backquote (`)</td>
|
<td>Backquote (`)</td>
|
||||||
<td>Exit</td>
|
<td>Run</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
For MacOS use 'Cmd' instead of 'Alt' key.
|
For MacOS use 'Cmd' instead of 'Alt' key.
|
||||||
<p>To the left of the global buttons, you find the "Options..." button.</p>
|
<p>To the left of the global buttons, you find the 'Options...' button.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<p><img src="graphics/debugger_options.png"></p>
|
<p><img src="graphics/debugger_options.png"></p>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -607,7 +608,7 @@ created a symbol file, you can use labels for the expression.</p>
|
||||||
|
|
||||||
<p>Example: You have got a label called "kernel". To break there,
|
<p>Example: You have got a label called "kernel". To break there,
|
||||||
the command is "break kernel". After you've set the breakpoint,
|
the command is "break kernel". After you've set the breakpoint,
|
||||||
exit the debugger ("quit" or click the Exit button). The emulator
|
exit the debugger (enter "run" or click the 'Run' button). The emulator
|
||||||
will run until it gets to the breakpoint, then it will enter the
|
will run until it gets to the breakpoint, then it will enter the
|
||||||
debugger with the Program Counter pointing to the instruction
|
debugger with the Program Counter pointing to the instruction
|
||||||
at the breakpoint.</p>
|
at the breakpoint.</p>
|
||||||
|
@ -754,6 +755,7 @@ that holds 'number of scanlines' on an actual console).</p>
|
||||||
<tr><td> _fcycles</td><td> Number of cycles since frame started</td></tr>
|
<tr><td> _fcycles</td><td> Number of cycles since frame started</td></tr>
|
||||||
<tr><td> _icycles</td><td> Number of cycles of last instruction</td></tr>
|
<tr><td> _icycles</td><td> Number of cycles of last instruction</td></tr>
|
||||||
<tr><td> _scan</td><td> Current scanline count</td></tr>
|
<tr><td> _scan</td><td> Current scanline count</td></tr>
|
||||||
|
<tr><td> _scanend</td><td> Scanline count at end of last frame</td></tr>
|
||||||
<tr><td> _scycles</td><td> Number of cycles in current scanline</td></tr>
|
<tr><td> _scycles</td><td> Number of cycles in current scanline</td></tr>
|
||||||
<tr><td> _vblank</td><td> Whether vertical blank is enabled (1 or 0)</td></tr>
|
<tr><td> _vblank</td><td> Whether vertical blank is enabled (1 or 0)</td></tr>
|
||||||
<tr><td> _vsync</td><td> Whether vertical sync is enabled (1 or 0)</td></tr>
|
<tr><td> _vsync</td><td> Whether vertical sync is enabled (1 or 0)</td></tr>
|
||||||
|
@ -906,7 +908,9 @@ Type "help 'cmd'" to see extended information about the given command.</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
a - Set Accumulator to <value>
|
a - Set Accumulator to <value>
|
||||||
|
aud - Mark 'AUD' range in disassembly
|
||||||
base - Set default number base to <base> (bin, dec, hex)
|
base - Set default number base to <base> (bin, dec, hex)
|
||||||
|
bcol - Mark 'BCOL' range in disassembly
|
||||||
break - Set/clear breakpoint at <address> and <bank>
|
break - Set/clear breakpoint at <address> and <bank>
|
||||||
breakif - Set/clear breakpoint on <condition>
|
breakif - Set/clear breakpoint on <condition>
|
||||||
breaklabel - Set/clear breakpoint on <address> (no mirrors, all banks)
|
breaklabel - Set/clear breakpoint on <address> (no mirrors, all banks)
|
||||||
|
@ -919,6 +923,7 @@ clearsavestateifs - Clear all savestate points
|
||||||
clearwatches - Clear all watches
|
clearwatches - Clear all watches
|
||||||
cls - Clear prompt area of text
|
cls - Clear prompt area of text
|
||||||
code - Mark 'CODE' range in disassembly
|
code - Mark 'CODE' range in disassembly
|
||||||
|
col - Mark 'COL' range in disassembly
|
||||||
colortest - Show value xx as TIA color
|
colortest - Show value xx as TIA color
|
||||||
d - Decimal Mode Flag: set (0 or 1), or toggle (no arg)
|
d - Decimal Mode Flag: set (0 or 1), or toggle (no arg)
|
||||||
data - Mark 'DATA' range in disassembly
|
data - Mark 'DATA' range in disassembly
|
||||||
|
@ -959,6 +964,7 @@ clearsavestateifs - Clear all savestate points
|
||||||
n - Negative Flag: set (0 or 1), or toggle (no arg)
|
n - Negative Flag: set (0 or 1), or toggle (no arg)
|
||||||
palette - Show current TIA palette
|
palette - Show current TIA palette
|
||||||
pc - Set Program Counter to address xx
|
pc - Set Program Counter to address xx
|
||||||
|
pcol - Mark 'PCOL' range in disassembly
|
||||||
pgfx - Mark 'PGFX' range in disassembly
|
pgfx - Mark 'PGFX' range in disassembly
|
||||||
print - Evaluate/print expression xx in hex/dec/binary
|
print - Evaluate/print expression xx in hex/dec/binary
|
||||||
ram - Show ZP RAM, or set address xx to yy1 [yy2 ...]
|
ram - Show ZP RAM, or set address xx to yy1 [yy2 ...]
|
||||||
|
@ -972,6 +978,7 @@ clearsavestateifs - Clear all savestate points
|
||||||
runtopc - Run until PC is set to value xx
|
runtopc - Run until PC is set to value xx
|
||||||
s - Set Stack Pointer to value xx
|
s - Set Stack Pointer to value xx
|
||||||
save - Save breaks, watches, traps and functions to file xx
|
save - Save breaks, watches, traps and functions to file xx
|
||||||
|
saveaccess - Save access counters to CSV file
|
||||||
saveconfig - Save Distella config file (with default name)
|
saveconfig - Save Distella config file (with default name)
|
||||||
savedis - Save Distella disassembly (with default name)
|
savedis - Save Distella disassembly (with default name)
|
||||||
saverom - Save (possibly patched) ROM (with default name)
|
saverom - Save (possibly patched) ROM (with default name)
|
||||||
|
@ -1022,8 +1029,8 @@ graphics and positions, and the playfield.</p>
|
||||||
of the displays are editable. You can even toggle individual bits in
|
of the displays are editable. You can even toggle individual bits in
|
||||||
the GRP0/1 and playfield registers (remember to double-click).</p>
|
the GRP0/1 and playfield registers (remember to double-click).</p>
|
||||||
|
|
||||||
<p>The group of buttons labelled "Strobes" allows you to write to any
|
<p>The buttons allow you to write to any of the strobe registers at
|
||||||
of the strobe registers at any time.</p>
|
any time.</p>
|
||||||
|
|
||||||
<p>The collision registers are displayed in decoded format, in a table.
|
<p>The collision registers are displayed in decoded format, in a table.
|
||||||
You can see exactly which objects have hit what. These are read-only
|
You can see exactly which objects have hit what. These are read-only
|
||||||
|
@ -1098,7 +1105,7 @@ or TV effects are enabled, you won't see the effects here; this shows the
|
||||||
<b>raw</b> TIA image only.</p>
|
<b>raw</b> TIA image only.</p>
|
||||||
|
|
||||||
<p>To e.g. watch the TIA draw the frame one scanline at a time, you can
|
<p>To e.g. watch the TIA draw the frame one scanline at a time, you can
|
||||||
use the "Scan+1" button, the prompt "scan" command or the Control-L key.</p>
|
use the 'Scan+1' button, the prompt "scan" command or the Control-L key.</p>
|
||||||
|
|
||||||
<p>You can also right-click anywhere in this window to show a context menu,
|
<p>You can also right-click anywhere in this window to show a context menu,
|
||||||
as illustrated:</p>
|
as illustrated:</p>
|
||||||
|
@ -1193,6 +1200,8 @@ the reason will be shown as follows:
|
||||||
<li>"WTrap:" for write traps</li>
|
<li>"WTrap:" for write traps</li>
|
||||||
<li>"RTrapIf:" for conditional read traps</li>
|
<li>"RTrapIf:" for conditional read traps</li>
|
||||||
<li>"WTrapIf:" for conditional write traps</li>
|
<li>"WTrapIf:" for conditional write traps</li>
|
||||||
|
<li>"RWP:" for reads from write ports</li>
|
||||||
|
<li>"WRP:" for writes to read ports</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
See the <a href="#BreakpointsEtc"><b>Breakpoints, watches and traps...</b></a>
|
See the <a href="#BreakpointsEtc"><b>Breakpoints, watches and traps...</b></a>
|
||||||
|
@ -1206,7 +1215,7 @@ section for details.</p>
|
||||||
<p><img src="graphics/debugger_cpuregs.png"></p>
|
<p><img src="graphics/debugger_cpuregs.png"></p>
|
||||||
<p>All the registers and flags are displayed, and can be changed by
|
<p>All the registers and flags are displayed, and can be changed by
|
||||||
double-clicking on them (to the left). Flags are toggled on double-click.
|
double-clicking on them (to the left). Flags are toggled on double-click.
|
||||||
Selected registers here can also be changed by using the "Data Operations" buttons,
|
Selected registers here can also be changed by using the 'Data Operations' buttons,
|
||||||
further described in (J). All items are shown in hex. Any label defined for the
|
further described in (J). All items are shown in hex. Any label defined for the
|
||||||
current PC value is shown to the right. Decimal and binary equivalents
|
current PC value is shown to the right. Decimal and binary equivalents
|
||||||
are shown for SP/A/X/Y to the right (first decimal, then binary).</p>
|
are shown for SP/A/X/Y to the right (first decimal, then binary).</p>
|
||||||
|
@ -1215,6 +1224,8 @@ registers. For example, consider the command 'LDA ($80),Y'. The operand of
|
||||||
the command resolves to some address, which isn't always easy to determine at
|
the command resolves to some address, which isn't always easy to determine at
|
||||||
first glance. The 'Src Addr' area shows the actual resulting operand/address
|
first glance. The 'Src Addr' area shows the actual resulting operand/address
|
||||||
being used with the given opcode.</p>
|
being used with the given opcode.</p>
|
||||||
|
<p>The destination address of the last write is shown besides 'Dest'.</p>
|
||||||
|
|
||||||
<p>There's not much else to say about the CPU Registers widget: if you know 6502
|
<p>There's not much else to say about the CPU Registers widget: if you know 6502
|
||||||
assembly, it's pretty self-explanatory. If you don't, well, you should
|
assembly, it's pretty self-explanatory. If you don't, well, you should
|
||||||
learn :)</p>
|
learn :)</p>
|
||||||
|
@ -1369,21 +1380,78 @@ can use. These are known as 'directives', and partly correspond to configuration
|
||||||
options from the standalone Distella program. They are listed in order of
|
options from the standalone Distella program. They are listed in order of
|
||||||
decreasing hierarchy:</p>
|
decreasing hierarchy:</p>
|
||||||
<table border="1" cellpadding=4>
|
<table border="1" cellpadding=4>
|
||||||
<tr><td><b>CODE</b></td><td>Addresses which have appeared in the program counter, or
|
<tr>
|
||||||
which tentatively can appear in the program counter. These can be edited in hex.</td></tr>
|
<td>
|
||||||
<tr><td><b>GFX</b></td><td>Addresses which contain data stored in the player graphics registers
|
<b>CODE</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which have appeared in the program counter, or
|
||||||
|
which tentatively can appear in the program counter. These can be edited in hex.
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>GFX</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which contain data stored in the player graphics registers
|
||||||
(GRP0/GRP1). These addresses are shown with a bitmap of the graphics, which
|
(GRP0/GRP1). These addresses are shown with a bitmap of the graphics, which
|
||||||
can be edited in either hex or binary. The bitmap is shown as large blocks.</td></tr>
|
can be edited in either hex or binary. The bitmap is shown as large blocks.
|
||||||
<tr><td><b>PGFX</b></td><td>Addresses which contain data stored in the playfield graphics registers
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>PGFX</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which contain data stored in the playfield graphics registers
|
||||||
(PF0/PF1/PF2). These addresses are shown with a bitmap of the graphics, which
|
(PF0/PF1/PF2). These addresses are shown with a bitmap of the graphics, which
|
||||||
can be edited in either hex or binary. The bitmap is shown as small dashes.</td></tr>
|
can be edited in either hex or binary. The bitmap is shown as small dashes.
|
||||||
<tr><td><b>DATA</b></td><td>Addresses used as an operand for some opcode. These can be edited
|
</td>
|
||||||
in hex.</td></tr>
|
</tr><tr>
|
||||||
<tr><td><b>ROW</b></td><td>Addresses not used as any of the above. These are shown up
|
<td>
|
||||||
to 8 per line, and cannot be edited.</td></tr>
|
<b>COL</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which contain data stored in the player color registers
|
||||||
|
(COLUP0/COLUP1). These addresses are shown as color constants, which
|
||||||
|
can be edited in hex. The color constant names are depending on the ROM's TV type.
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>PCOL</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which contain data stored in the playfield color register
|
||||||
|
(COLUPF). These addresses are shown as color constants, which
|
||||||
|
can be edited in hex. The color constant names are depending on the ROM's TV type.
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>BCOL</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which contain data stored in the background color register
|
||||||
|
(COLUBK). These addresses are shown as color constants, which
|
||||||
|
can be edited in hex. The color constant names are depending on the ROM's TV type.
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>AUD</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses which contain data stored in the audio registers
|
||||||
|
(AUDC0/AUDC1/AUDF0/AUDF1/AUDV0/AUDV1). These can be edited
|
||||||
|
in hex.
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>DATA</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses used as an operand for some opcode. These can be edited
|
||||||
|
in hex.
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>
|
||||||
|
<b>ROW</b>
|
||||||
|
</td><td>
|
||||||
|
Addresses not used as any of the above. These are shown up
|
||||||
|
to 8 per line and cannot be edited.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<p>For code sections, the 6502 mnemonic will be UPPERCASE for all standard instructions,
|
<p>For code sections, the 6502 mnemonic will be UPPERCASE for all standard instructions,
|
||||||
or lowercase for "illegal" 6502 instructions (like "dcp"). If automatic resolving
|
or lowercase for "illegal" 6502 instructions (like "dcp"). If automatic resolving
|
||||||
of code sections has been disabled for any reason, you'll likely see a lot
|
of code sections has been disabled for any reason, you'll likely see a lot
|
||||||
|
@ -1428,13 +1496,13 @@ anywhere in the listing:</p>
|
||||||
<p>The following options are available:</p>
|
<p>The following options are available:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Set PC @ current line</b>: Set the Program Counter to the address of the
|
<li><b>Set PC @ current line</b>: Set the Program Counter to the address of the
|
||||||
disassembly line where the mouse was clicked (highlighted in yellow).</li>
|
disassembly line where the mouse was clicked.</li>
|
||||||
|
|
||||||
<li><b>RunTo PC @ current line</b>: Single-step through code until the Program Counter
|
<li><b>RunTo PC @ current line</b>: Single-step through code until the Program Counter
|
||||||
matches the address of the disassembly line where the mouse was clicked (highlighted in yellow)</li>
|
matches the address of the disassembly line where the mouse was clicked.</li>
|
||||||
|
|
||||||
<li><b>Re-disassemble</b>: Self-explanatory; force the current bank to be
|
<li><b>Disassemble @ current line</b>: Disassemble from the disassembly line where the mouse was clicked.
|
||||||
disassembled, regardless of whether anything has changed.</li>
|
This allows to fill gaps in the disassembly and is most useful for bankswitched ROMs.</li>
|
||||||
|
|
||||||
<li><b>Show tentative code</b>: Allow Distella to do a static analysis/disassembly.</li>
|
<li><b>Show tentative code</b>: Allow Distella to do a static analysis/disassembly.</li>
|
||||||
|
|
||||||
|
@ -1445,7 +1513,8 @@ isn't already a defined label).</li>
|
||||||
in either binary or hexidecimal.</li>
|
in either binary or hexidecimal.</li>
|
||||||
|
|
||||||
<li><b>Use address relocation</b>: Corresponds to the Distella '-r' option
|
<li><b>Use address relocation</b>: Corresponds to the Distella '-r' option
|
||||||
(Relocate calls out of address range).</li>
|
(Relocate calls out of address range).</br>
|
||||||
|
Note: The code will continue to run fine, but the ROM image will be altered.</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -1520,12 +1589,12 @@ the RAM in the DPC scheme is not viewable by the 6507, so its addresses start fr
|
||||||
<br>
|
<br>
|
||||||
<h1><a name="DistellaConfiguration">Distella Configuration Files</a></h1>
|
<h1><a name="DistellaConfiguration">Distella Configuration Files</a></h1>
|
||||||
<p>As mentioned in <a href="#Disassembly"><b>ROM Disassembly</b></a>, Stella supports the following directives:
|
<p>As mentioned in <a href="#Disassembly"><b>ROM Disassembly</b></a>, Stella supports the following directives:
|
||||||
CODE/GFX/PGFX/DATA/ROW. While the debugger will try to automatically mark address
|
CODE, GFX, PGFX, COL, PCOL, BCOL, AUD, DATA, ROW. While the debugger will try to automatically mark address
|
||||||
space with the appropriate directive, there are times when it will fail. There are
|
space with the appropriate directive, there are times when it will fail. There are
|
||||||
several options in this case:</p>
|
several options in this case:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li><b>Manually set the directives</b>: Directives can be set in the debugger
|
<li><b>Manually set the directives</b>: Directives can be set in the debugger
|
||||||
prompt with the code/gfx/pgfx/data/row commands. These accept an address range
|
prompt with the aud/code/col/bcol/gfx/pcol/pgfx/data/row commands. These accept an address range
|
||||||
for the given directive type. Setting a range with the same type a second time
|
for the given directive type. Setting a range with the same type a second time
|
||||||
will remove that directive from the range.</li>
|
will remove that directive from the range.</li>
|
||||||
<li><b>Use configuration files</b>: Configuration files can be used to automatically
|
<li><b>Use configuration files</b>: Configuration files can be used to automatically
|
||||||
|
@ -1564,7 +1633,7 @@ but it helps to know at least a little about 6502 programming.</p>
|
||||||
<li>Enter the debugger by pressing the ` (backquote) key. Don't get
|
<li>Enter the debugger by pressing the ` (backquote) key. Don't get
|
||||||
killed before you do this, though. You should still have all 5 lives.</li>
|
killed before you do this, though. You should still have all 5 lives.</li>
|
||||||
|
|
||||||
<li>In the RAM display, click the "Search" button and enter "5" for input.
|
<li>In the RAM display, click the 'Search' button and enter "5" for input.
|
||||||
This searches RAM for your value and highlights all addresses that match
|
This searches RAM for your value and highlights all addresses that match
|
||||||
the input. You should see two addresses highlighted: "00a5" and "00ba".
|
the input. You should see two addresses highlighted: "00a5" and "00ba".
|
||||||
These are the only two addresses that currently have the value 5, so they're
|
These are the only two addresses that currently have the value 5, so they're
|
||||||
|
@ -1580,7 +1649,7 @@ but it helps to know at least a little about 6502 programming.</p>
|
||||||
<li>Get killed! Ram an enemy tank, or let him shoot you. Wait for
|
<li>Get killed! Ram an enemy tank, or let him shoot you. Wait for
|
||||||
the explosion to finish. You will now have 4 lives.</li>
|
the explosion to finish. You will now have 4 lives.</li>
|
||||||
|
|
||||||
<li>Enter the debugger again. Click the "Compare" button in RAM widget and enter
|
<li>Enter the debugger again. Click the 'Compare...' button in RAM widget and enter
|
||||||
a value of 4. Now the RAM widget should only show one highlighted address:
|
a value of 4. Now the RAM widget should only show one highlighted address:
|
||||||
"00ba". What we did was search within our previous results (the ones that
|
"00ba". What we did was search within our previous results (the ones that
|
||||||
were 5 before) for the new value 4. Address $00ba used to have the value 5,
|
were 5 before) for the new value 4. Address $00ba used to have the value 5,
|
||||||
|
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.5 KiB |
1056
docs/index.html
|
@ -58,7 +58,7 @@
|
||||||
|
|
||||||
<center><h1>Stella for RetroN 77</h1></center>
|
<center><h1>Stella for RetroN 77</h1></center>
|
||||||
<center><h2>Atari 2600 VCS emulator</h2></center>
|
<center><h2>Atari 2600 VCS emulator</h2></center>
|
||||||
<center>Release 6.1 Beta 1</center>
|
<center>Release 6.2.1</center>
|
||||||
<center><h2>Quick Navigation Guide</h2></center>
|
<center><h2>Quick Navigation Guide</h2></center>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|
|
@ -36,45 +36,48 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
|
||||||
: Dialog(osystem, parent, font, "Cheat codes")
|
: Dialog(osystem, parent, font, "Cheat codes")
|
||||||
{
|
{
|
||||||
const int lineHeight = font.getLineHeight(),
|
const int lineHeight = font.getLineHeight(),
|
||||||
|
fontHeight = font.getFontHeight(),
|
||||||
fontWidth = font.getMaxCharWidth(),
|
fontWidth = font.getMaxCharWidth(),
|
||||||
buttonWidth = font.getStringWidth("Defaults") + 20,
|
buttonWidth = font.getStringWidth("One shot ") + fontWidth * 2.5,
|
||||||
buttonHeight = font.getLineHeight() + 4;
|
buttonHeight = font.getLineHeight() * 1.25;
|
||||||
const int HBORDER = 10;
|
const int VBORDER = fontHeight / 2;
|
||||||
const int VBORDER = 10 + _th;
|
const int HBORDER = fontWidth * 1.25;
|
||||||
|
const int VGAP = fontHeight / 4;
|
||||||
|
|
||||||
int xpos, ypos;
|
int xpos, ypos;
|
||||||
WidgetArray wid;
|
WidgetArray wid;
|
||||||
ButtonWidget* b;
|
ButtonWidget* b;
|
||||||
|
|
||||||
// Set real dimensions
|
// Set real dimensions
|
||||||
_w = 45 * fontWidth + HBORDER * 2;
|
_w = 45 * fontWidth + HBORDER * 2;
|
||||||
_h = 11 * (lineHeight + 4) + VBORDER;
|
_h = _th + 11 * (lineHeight + 4) + VBORDER * 2;
|
||||||
|
|
||||||
// List of cheats, with checkboxes to enable/disable
|
// List of cheats, with checkboxes to enable/disable
|
||||||
xpos = HBORDER; ypos = VBORDER;
|
xpos = HBORDER; ypos = _th + VBORDER;
|
||||||
myCheatList =
|
myCheatList =
|
||||||
new CheckListWidget(this, font, xpos, ypos, _w - buttonWidth - HBORDER * 2 - 8,
|
new CheckListWidget(this, font, xpos, ypos, _w - buttonWidth - HBORDER * 2 - fontWidth,
|
||||||
_h - 2*buttonHeight - VBORDER);
|
_h - _th - buttonHeight - VBORDER * 3);
|
||||||
myCheatList->setEditable(false);
|
myCheatList->setEditable(false);
|
||||||
wid.push_back(myCheatList);
|
wid.push_back(myCheatList);
|
||||||
|
|
||||||
xpos += myCheatList->getWidth() + 8; ypos = VBORDER;
|
xpos += myCheatList->getWidth() + fontWidth; ypos = _th + VBORDER;
|
||||||
|
|
||||||
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||||
"Add" + ELLIPSIS, kAddCheatCmd);
|
"Add" + ELLIPSIS, kAddCheatCmd);
|
||||||
wid.push_back(b);
|
wid.push_back(b);
|
||||||
ypos += lineHeight + 8;
|
ypos += lineHeight + VGAP * 2;
|
||||||
|
|
||||||
myEditButton =
|
myEditButton =
|
||||||
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||||
"Edit" + ELLIPSIS, kEditCheatCmd);
|
"Edit" + ELLIPSIS, kEditCheatCmd);
|
||||||
wid.push_back(myEditButton);
|
wid.push_back(myEditButton);
|
||||||
ypos += lineHeight + 8;
|
ypos += lineHeight + VGAP * 2;
|
||||||
|
|
||||||
myRemoveButton =
|
myRemoveButton =
|
||||||
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||||
"Remove", kRemCheatCmd);
|
"Remove", kRemCheatCmd);
|
||||||
wid.push_back(myRemoveButton);
|
wid.push_back(myRemoveButton);
|
||||||
ypos += lineHeight + 8 * 3;
|
ypos += lineHeight + VGAP * 2 * 3;
|
||||||
|
|
||||||
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||||
"One shot" + ELLIPSIS, kAddOneShotCmd);
|
"One shot" + ELLIPSIS, kAddOneShotCmd);
|
||||||
|
|
|
@ -213,10 +213,9 @@ void CheatManager::enable(const string& code, bool enable)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void CheatManager::loadCheatDatabase()
|
void CheatManager::loadCheatDatabase()
|
||||||
{
|
{
|
||||||
const string& cheatfile = myOSystem.cheatFile();
|
stringstream in;
|
||||||
ifstream in(cheatfile);
|
try { myOSystem.cheatFile().read(in); }
|
||||||
if(!in)
|
catch(...) { return; }
|
||||||
return;
|
|
||||||
|
|
||||||
string line, md5, cheat;
|
string line, md5, cheat;
|
||||||
string::size_type one, two, three, four;
|
string::size_type one, two, three, four;
|
||||||
|
@ -253,13 +252,12 @@ void CheatManager::saveCheatDatabase()
|
||||||
if(!myListIsDirty)
|
if(!myListIsDirty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const string& cheatfile = myOSystem.cheatFile();
|
stringstream out;
|
||||||
ofstream out(cheatfile);
|
|
||||||
if(!out)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for(const auto& iter: myCheatMap)
|
for(const auto& iter: myCheatMap)
|
||||||
out << "\"" << iter.first << "\" " << "\"" << iter.second << "\"" << endl;
|
out << "\"" << iter.first << "\" " << "\"" << iter.second << "\"" << endl;
|
||||||
|
|
||||||
|
try { myOSystem.cheatFile().write(out); }
|
||||||
|
catch(...) { return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -154,6 +154,12 @@ uInt32 AudioSettings::volume() const
|
||||||
return lboundInt(mySettings.getInt(SETTING_VOLUME), 0);
|
return lboundInt(mySettings.getInt(SETTING_VOLUME), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt32 AudioSettings::device() const
|
||||||
|
{
|
||||||
|
return mySettings.getInt(SETTING_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool AudioSettings::enabled() const
|
bool AudioSettings::enabled() const
|
||||||
{
|
{
|
||||||
|
@ -285,6 +291,14 @@ void AudioSettings::setVolume(uInt32 volume)
|
||||||
normalize(mySettings);
|
normalize(mySettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void AudioSettings::setDevice(uInt32 device)
|
||||||
|
{
|
||||||
|
if(!myIsPersistent) return;
|
||||||
|
|
||||||
|
mySettings.setValue(SETTING_DEVICE, device);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void AudioSettings::setEnabled(bool isEnabled)
|
void AudioSettings::setEnabled(bool isEnabled)
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,6 +48,7 @@ class AudioSettings
|
||||||
static constexpr const char* SETTING_RESAMPLING_QUALITY = "audio.resampling_quality";
|
static constexpr const char* SETTING_RESAMPLING_QUALITY = "audio.resampling_quality";
|
||||||
static constexpr const char* SETTING_STEREO = "audio.stereo";
|
static constexpr const char* SETTING_STEREO = "audio.stereo";
|
||||||
static constexpr const char* SETTING_VOLUME = "audio.volume";
|
static constexpr const char* SETTING_VOLUME = "audio.volume";
|
||||||
|
static constexpr const char* SETTING_DEVICE = "audio.device";
|
||||||
static constexpr const char* SETTING_ENABLED = "audio.enabled";
|
static constexpr const char* SETTING_ENABLED = "audio.enabled";
|
||||||
static constexpr const char* SETTING_DPC_PITCH = "audio.dpc_pitch";
|
static constexpr const char* SETTING_DPC_PITCH = "audio.dpc_pitch";
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ class AudioSettings
|
||||||
static constexpr ResamplingQuality DEFAULT_RESAMPLING_QUALITY = ResamplingQuality::lanczos_2;
|
static constexpr ResamplingQuality DEFAULT_RESAMPLING_QUALITY = ResamplingQuality::lanczos_2;
|
||||||
static constexpr bool DEFAULT_STEREO = false;
|
static constexpr bool DEFAULT_STEREO = false;
|
||||||
static constexpr uInt32 DEFAULT_VOLUME = 80;
|
static constexpr uInt32 DEFAULT_VOLUME = 80;
|
||||||
|
static constexpr uInt32 DEFAULT_DEVICE = 0;
|
||||||
static constexpr bool DEFAULT_ENABLED = true;
|
static constexpr bool DEFAULT_ENABLED = true;
|
||||||
static constexpr uInt32 DEFAULT_DPC_PITCH = 20000;
|
static constexpr uInt32 DEFAULT_DPC_PITCH = 20000;
|
||||||
|
|
||||||
|
@ -87,6 +89,8 @@ class AudioSettings
|
||||||
|
|
||||||
uInt32 volume() const;
|
uInt32 volume() const;
|
||||||
|
|
||||||
|
uInt32 device() const;
|
||||||
|
|
||||||
bool enabled() const;
|
bool enabled() const;
|
||||||
|
|
||||||
uInt32 dpcPitch() const;
|
uInt32 dpcPitch() const;
|
||||||
|
@ -109,6 +113,8 @@ class AudioSettings
|
||||||
|
|
||||||
void setVolume(uInt32 volume);
|
void setVolume(uInt32 volume);
|
||||||
|
|
||||||
|
void setDevice(uInt32 device);
|
||||||
|
|
||||||
void setEnabled(bool isEnabled);
|
void setEnabled(bool isEnabled);
|
||||||
|
|
||||||
void setPersistent(bool isPersistent);
|
void setPersistent(bool isPersistent);
|
||||||
|
|
|
@ -126,6 +126,21 @@ void FilesystemNodeZIP::setFlags(const string& zipfile,
|
||||||
_error = zip_error::NOT_READABLE;
|
_error = zip_error::NOT_READABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool FilesystemNodeZIP::exists() const
|
||||||
|
{
|
||||||
|
if(_realNode && _realNode->exists())
|
||||||
|
{
|
||||||
|
// We need to inspect the actual path, not just the ZIP file itself
|
||||||
|
myZipHandler->open(_zipFile);
|
||||||
|
while(myZipHandler->hasNext())
|
||||||
|
if(BSPF::startsWithIgnoreCase(myZipHandler->next(), _virtualPath))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
|
bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
|
||||||
{
|
{
|
||||||
|
@ -181,7 +196,34 @@ size_t FilesystemNodeZIP::read(ByteBuffer& image) const
|
||||||
while(myZipHandler->hasNext() && !found)
|
while(myZipHandler->hasNext() && !found)
|
||||||
found = myZipHandler->next() == _virtualPath;
|
found = myZipHandler->next() == _virtualPath;
|
||||||
|
|
||||||
return found ? uInt32(myZipHandler->decompress(image)) : 0; // TODO: 64bit
|
return found ? myZipHandler->decompress(image) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
size_t FilesystemNodeZIP::read(stringstream& image) const
|
||||||
|
{
|
||||||
|
// For now, we just read into a buffer and store in the stream
|
||||||
|
// TODO: maybe there's a more efficient way to do this?
|
||||||
|
ByteBuffer buffer;
|
||||||
|
size_t size = read(buffer);
|
||||||
|
if(size > 0)
|
||||||
|
image.write(reinterpret_cast<char*>(buffer.get()), size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
size_t FilesystemNodeZIP::write(const ByteBuffer& buffer, size_t size) const
|
||||||
|
{
|
||||||
|
// TODO: Not yet implemented
|
||||||
|
throw runtime_error("ZIP file not writable");
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
size_t FilesystemNodeZIP::write(const stringstream& buffer) const
|
||||||
|
{
|
||||||
|
// TODO: Not yet implemented
|
||||||
|
throw runtime_error("ZIP file not writable");
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -42,7 +42,7 @@ class FilesystemNodeZIP : public AbstractFSNode
|
||||||
*/
|
*/
|
||||||
explicit FilesystemNodeZIP(const string& path);
|
explicit FilesystemNodeZIP(const string& path);
|
||||||
|
|
||||||
bool exists() const override { return _realNode && _realNode->exists(); }
|
bool exists() const override;
|
||||||
const string& getName() const override { return _name; }
|
const string& getName() const override { return _name; }
|
||||||
void setName(const string& name) override { _name = name; }
|
void setName(const string& name) override { _name = name; }
|
||||||
const string& getPath() const override { return _path; }
|
const string& getPath() const override { return _path; }
|
||||||
|
@ -63,6 +63,9 @@ class FilesystemNodeZIP : public AbstractFSNode
|
||||||
AbstractFSNodePtr getParent() const override;
|
AbstractFSNodePtr getParent() const override;
|
||||||
|
|
||||||
size_t read(ByteBuffer& image) const override;
|
size_t read(ByteBuffer& image) const override;
|
||||||
|
size_t read(stringstream& image) const override;
|
||||||
|
size_t write(const ByteBuffer& buffer, size_t size) const override;
|
||||||
|
size_t write(const stringstream& buffer) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FilesystemNodeZIP(const string& zipfile, const string& virtualpath,
|
FilesystemNodeZIP(const string& zipfile, const string& virtualpath,
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "SDL_lib.hxx"
|
#include "SDL_lib.hxx"
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Logger.hxx"
|
#include "Logger.hxx"
|
||||||
|
@ -48,8 +50,6 @@ FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem)
|
||||||
// since the structure may be needed before any FBSurface's have
|
// since the structure may be needed before any FBSurface's have
|
||||||
// been created
|
// been created
|
||||||
myPixelFormat = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888);
|
myPixelFormat = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
|
||||||
myWindowedPos = myOSystem.settings().getPoint("windowedpos");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -101,19 +101,32 @@ void FrameBufferSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
|
||||||
int numModes = SDL_GetNumDisplayModes(i);
|
int numModes = SDL_GetNumDisplayModes(i);
|
||||||
ostringstream s;
|
ostringstream s;
|
||||||
|
|
||||||
s << "Supported video modes for display " << i << ":";
|
s << "Supported video modes (" << numModes << ") for display " << i << ":";
|
||||||
Logger::debug(s.str());
|
|
||||||
|
string lastRes = "";
|
||||||
|
|
||||||
for (int m = 0; m < numModes; m++)
|
for (int m = 0; m < numModes; m++)
|
||||||
{
|
{
|
||||||
SDL_DisplayMode mode;
|
SDL_DisplayMode mode;
|
||||||
|
ostringstream res;
|
||||||
|
|
||||||
SDL_GetDisplayMode(i, m, &mode);
|
SDL_GetDisplayMode(i, m, &mode);
|
||||||
s.str("");
|
res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
|
||||||
s << " " << m << ": " << mode.w << "x" << mode.h << "@" << mode.refresh_rate << "Hz";
|
|
||||||
if (mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate)
|
if(lastRes != res.str())
|
||||||
s << " (active)";
|
{
|
||||||
Logger::debug(s.str());
|
Logger::debug(s.str());
|
||||||
|
s.str("");
|
||||||
|
lastRes = res.str();
|
||||||
|
s << " " << lastRes << ": ";
|
||||||
}
|
}
|
||||||
|
s << mode.refresh_rate << "Hz";
|
||||||
|
if(mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate)
|
||||||
|
s << "* ";
|
||||||
|
else
|
||||||
|
s << " ";
|
||||||
|
}
|
||||||
|
Logger::debug(s.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now get the maximum windowed desktop resolution
|
// Now get the maximum windowed desktop resolution
|
||||||
|
@ -183,27 +196,34 @@ void FrameBufferSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Int32 FrameBufferSDL2::getCurrentDisplayIndex()
|
bool FrameBufferSDL2::isCurrentWindowPositioned() const
|
||||||
|
{
|
||||||
|
ASSERT_MAIN_THREAD;
|
||||||
|
|
||||||
|
return !myCenter
|
||||||
|
&& myWindow && !(SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Common::Point FrameBufferSDL2::getCurrentWindowPos() const
|
||||||
|
{
|
||||||
|
ASSERT_MAIN_THREAD;
|
||||||
|
|
||||||
|
Common::Point pos;
|
||||||
|
|
||||||
|
SDL_GetWindowPosition(myWindow, &pos.x, &pos.y);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Int32 FrameBufferSDL2::getCurrentDisplayIndex() const
|
||||||
{
|
{
|
||||||
ASSERT_MAIN_THREAD;
|
ASSERT_MAIN_THREAD;
|
||||||
|
|
||||||
return SDL_GetWindowDisplayIndex(myWindow);
|
return SDL_GetWindowDisplayIndex(myWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void FrameBufferSDL2::updateWindowedPos()
|
|
||||||
{
|
|
||||||
ASSERT_MAIN_THREAD;
|
|
||||||
|
|
||||||
// only save if the window is not centered and not in full screen mode
|
|
||||||
if (!myCenter && myWindow && !(SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP))
|
|
||||||
{
|
|
||||||
// save current windowed position
|
|
||||||
SDL_GetWindowPosition(myWindow, &myWindowedPos.x, &myWindowedPos.y);
|
|
||||||
myOSystem.settings().setValue("windowedpos", myWindowedPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
||||||
{
|
{
|
||||||
|
@ -213,31 +233,13 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
||||||
if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
|
if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Int32 displayIndex = mode.fsIndex;
|
const bool fullScreen = mode.fsIndex != -1;
|
||||||
if (displayIndex == -1)
|
bool forceCreateRenderer = false;
|
||||||
{
|
|
||||||
// windowed mode
|
|
||||||
if (myWindow)
|
|
||||||
{
|
|
||||||
// Show it on same screen as the previous window
|
|
||||||
displayIndex = SDL_GetWindowDisplayIndex(myWindow);
|
|
||||||
}
|
|
||||||
if (displayIndex < 0)
|
|
||||||
{
|
|
||||||
// fallback to the last used screen if still existing
|
|
||||||
displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt("display"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save and get last windowed window's position
|
// Get windowed window's last display
|
||||||
updateWindowedPos();
|
Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey()));
|
||||||
|
// Get windowed window's last position
|
||||||
// Always recreate renderer (some systems need this)
|
myWindowedPos = myOSystem.settings().getPoint(getPositionKey());
|
||||||
if(myRenderer)
|
|
||||||
{
|
|
||||||
SDL_DestroyRenderer(myRenderer);
|
|
||||||
myRenderer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int posX, posY;
|
int posX, posY;
|
||||||
|
|
||||||
|
@ -249,7 +251,7 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
||||||
posX = myWindowedPos.x;
|
posX = myWindowedPos.x;
|
||||||
posY = myWindowedPos.y;
|
posY = myWindowedPos.y;
|
||||||
|
|
||||||
// make sure the window is at least partially visibile
|
// Make sure the window is at least partially visibile
|
||||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||||
|
|
||||||
for (int display = SDL_GetNumVideoDisplays() - 1; display >= 0; display--)
|
for (int display = SDL_GetNumVideoDisplays() - 1; display >= 0; display--)
|
||||||
|
@ -267,47 +269,45 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
||||||
posX = BSPF::clamp(posX, x0 - Int32(mode.screen.w) + 50, x1 - 50);
|
posX = BSPF::clamp(posX, x0 - Int32(mode.screen.w) + 50, x1 - 50);
|
||||||
posY = BSPF::clamp(posY, y0 + 50, y1 - 50);
|
posY = BSPF::clamp(posY, y0 + 50, y1 - 50);
|
||||||
}
|
}
|
||||||
uInt32 flags = mode.fsIndex != -1 ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
|
|
||||||
flags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
|
||||||
|
|
||||||
// macOS seems to have issues with destroying the window, and wants to
|
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||||
// keep the same handle
|
SDL_DisplayMode adaptedSdlMode;
|
||||||
// Problem is, doing so on other platforms results in flickering when
|
const bool shouldAdapt = fullScreen && myOSystem.settings().getBool("tia.fs_refresh")
|
||||||
// toggling fullscreen windowed mode
|
&& gameRefreshRate()
|
||||||
// So we have a special case for macOS
|
// take care of 59.94 Hz
|
||||||
#ifndef BSPF_MACOS
|
&& refreshRate() % gameRefreshRate() != 0 && refreshRate() % (gameRefreshRate() - 1) != 0;
|
||||||
// Don't re-create the window if its size hasn't changed, as it's not
|
const bool adaptRefresh = shouldAdapt && adaptRefreshRate(displayIndex, adaptedSdlMode);
|
||||||
// necessary, and causes flashing in fullscreen mode
|
#else
|
||||||
|
const bool adaptRefresh = false;
|
||||||
|
#endif
|
||||||
|
const uInt32 flags = SDL_WINDOW_ALLOW_HIGHDPI
|
||||||
|
| (fullScreen ? adaptRefresh ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||||
|
|
||||||
|
// Don't re-create the window if its display and size hasn't changed,
|
||||||
|
// as it's not necessary, and causes flashing in fullscreen mode
|
||||||
if(myWindow)
|
if(myWindow)
|
||||||
{
|
{
|
||||||
|
const int d = SDL_GetWindowDisplayIndex(myWindow);
|
||||||
int w, h;
|
int w, h;
|
||||||
|
|
||||||
SDL_GetWindowSize(myWindow, &w, &h);
|
SDL_GetWindowSize(myWindow, &w, &h);
|
||||||
if(uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h)
|
if(d != displayIndex || uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h
|
||||||
|
|| adaptRefresh)
|
||||||
{
|
{
|
||||||
SDL_DestroyWindow(myWindow);
|
SDL_DestroyWindow(myWindow);
|
||||||
myWindow = nullptr;
|
myWindow = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(myWindow)
|
if(myWindow)
|
||||||
{
|
{
|
||||||
// Even though window size stayed the same, the title may have changed
|
// Even though window size stayed the same, the title may have changed
|
||||||
SDL_SetWindowTitle(myWindow, title.c_str());
|
SDL_SetWindowTitle(myWindow, title.c_str());
|
||||||
SDL_SetWindowPosition(myWindow, posX, posY);
|
SDL_SetWindowPosition(myWindow, posX, posY);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
// macOS wants to *never* re-create the window
|
|
||||||
// This sometimes results in the window being resized *after* it's displayed,
|
|
||||||
// but at least the code works and doesn't crash
|
|
||||||
if(myWindow)
|
|
||||||
{
|
|
||||||
SDL_SetWindowFullscreen(myWindow, flags);
|
|
||||||
SDL_SetWindowSize(myWindow, mode.screen.w, mode.screen.h);
|
|
||||||
SDL_SetWindowPosition(myWindow, posX, posY);
|
|
||||||
SDL_SetWindowTitle(myWindow, title.c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
forceCreateRenderer = true;
|
||||||
myWindow = SDL_CreateWindow(title.c_str(), posX, posY,
|
myWindow = SDL_CreateWindow(title.c_str(), posX, posY,
|
||||||
mode.screen.w, mode.screen.h, flags);
|
mode.screen.w, mode.screen.h, flags);
|
||||||
if(myWindow == nullptr)
|
if(myWindow == nullptr)
|
||||||
|
@ -316,13 +316,114 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
||||||
Logger::error(msg);
|
Logger::error(msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWindowIcon();
|
setWindowIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||||
|
if(adaptRefresh)
|
||||||
|
{
|
||||||
|
// Switch to mode for adapted refresh rate
|
||||||
|
if(SDL_SetWindowDisplayMode(myWindow, &adaptedSdlMode) != 0)
|
||||||
|
{
|
||||||
|
Logger::error("ERROR: Display refresh rate change failed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ostringstream msg;
|
||||||
|
|
||||||
|
msg << "Display refresh rate changed to " << adaptedSdlMode.refresh_rate << " Hz";
|
||||||
|
Logger::info(msg.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return createRenderer(forceCreateRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool FrameBufferSDL2::adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode)
|
||||||
|
{
|
||||||
|
SDL_DisplayMode sdlMode;
|
||||||
|
|
||||||
|
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) != 0)
|
||||||
|
{
|
||||||
|
Logger::error("ERROR: Display mode could not be retrieved");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int currentRefreshRate = sdlMode.refresh_rate;
|
||||||
|
const int wantedRefreshRate = gameRefreshRate();
|
||||||
|
// Take care of rounded refresh rates (e.g. 59.94 Hz)
|
||||||
|
float factor = std::min(float(currentRefreshRate) / wantedRefreshRate,
|
||||||
|
float(currentRefreshRate) / (wantedRefreshRate - 1));
|
||||||
|
// Calculate difference taking care of integer factors (e.g. 100/120)
|
||||||
|
float bestDiff = std::abs(factor - std::round(factor)) / factor;
|
||||||
|
bool adapt = false;
|
||||||
|
|
||||||
|
// Display refresh rate should be an integer factor of the game's refresh rate
|
||||||
|
// Note: Modes are scanned with size being first priority,
|
||||||
|
// therefore the size will never change.
|
||||||
|
// Check for integer factors 1 (60/50 Hz) and 2 (120/100 Hz)
|
||||||
|
for(int m = 1; m <= 2; ++m)
|
||||||
|
{
|
||||||
|
SDL_DisplayMode closestSdlMode;
|
||||||
|
|
||||||
|
sdlMode.refresh_rate = wantedRefreshRate * m;
|
||||||
|
if(SDL_GetClosestDisplayMode(displayIndex, &sdlMode, &closestSdlMode) == nullptr)
|
||||||
|
{
|
||||||
|
Logger::error("ERROR: Closest display mode could not be retrieved");
|
||||||
|
return adapt;
|
||||||
|
}
|
||||||
|
factor = std::min(float(sdlMode.refresh_rate) / sdlMode.refresh_rate,
|
||||||
|
float(sdlMode.refresh_rate) / (sdlMode.refresh_rate - 1));
|
||||||
|
const float diff = std::abs(factor - std::round(factor)) / factor;
|
||||||
|
if(diff < bestDiff)
|
||||||
|
{
|
||||||
|
bestDiff = diff;
|
||||||
|
adaptedSdlMode = closestSdlMode;
|
||||||
|
adapt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//cerr << "refresh rate adapt ";
|
||||||
|
//if(adapt)
|
||||||
|
// cerr << "required (" << currentRefreshRate << " Hz -> " << adaptedSdlMode.refresh_rate << " Hz)";
|
||||||
|
//else
|
||||||
|
// cerr << "not required/possible";
|
||||||
|
//cerr << endl;
|
||||||
|
|
||||||
|
// Only change if the display supports a better refresh rate
|
||||||
|
return adapt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool FrameBufferSDL2::createRenderer(bool force)
|
||||||
|
{
|
||||||
|
// A new renderer is only created when necessary:
|
||||||
|
// - new myWindow (force = true)
|
||||||
|
// - no renderer existing
|
||||||
|
// - different renderer flags
|
||||||
|
// - different renderer name
|
||||||
|
bool recreate = force || myRenderer == nullptr;
|
||||||
uInt32 renderFlags = SDL_RENDERER_ACCELERATED;
|
uInt32 renderFlags = SDL_RENDERER_ACCELERATED;
|
||||||
if(myOSystem.settings().getBool("vsync")) // V'synced blits option
|
|
||||||
renderFlags |= SDL_RENDERER_PRESENTVSYNC;
|
|
||||||
const string& video = myOSystem.settings().getString("video"); // Render hint
|
const string& video = myOSystem.settings().getString("video"); // Render hint
|
||||||
|
SDL_RendererInfo renderInfo;
|
||||||
|
|
||||||
|
if(myOSystem.settings().getBool("vsync")
|
||||||
|
&& !myOSystem.settings().getBool("turbo")) // V'synced blits option
|
||||||
|
renderFlags |= SDL_RENDERER_PRESENTVSYNC;
|
||||||
|
|
||||||
|
// check renderer flags and name
|
||||||
|
recreate |= (SDL_GetRendererInfo(myRenderer, &renderInfo) != 0)
|
||||||
|
|| ((renderInfo.flags & (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)) != renderFlags
|
||||||
|
|| (video != renderInfo.name));
|
||||||
|
|
||||||
|
if(recreate)
|
||||||
|
{
|
||||||
|
//cerr << "Create new renderer for buffer type #" << int(myBufferType) << endl;
|
||||||
|
if(myRenderer)
|
||||||
|
SDL_DestroyRenderer(myRenderer);
|
||||||
|
|
||||||
if(video != "")
|
if(video != "")
|
||||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
|
||||||
|
|
||||||
|
@ -337,9 +438,11 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
||||||
Logger::error(msg);
|
Logger::error(msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
SDL_RendererInfo renderinfo;
|
SDL_RendererInfo renderinfo;
|
||||||
|
|
||||||
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
|
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
|
||||||
myOSystem.settings().setValue("video", renderinfo.name);
|
myOSystem.settings().setValue("video", renderinfo.name);
|
||||||
|
|
||||||
|
@ -407,6 +510,36 @@ bool FrameBufferSDL2::fullScreen() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
int FrameBufferSDL2::refreshRate() const
|
||||||
|
{
|
||||||
|
ASSERT_MAIN_THREAD;
|
||||||
|
|
||||||
|
const uInt32 displayIndex = SDL_GetWindowDisplayIndex(myWindow);
|
||||||
|
SDL_DisplayMode sdlMode;
|
||||||
|
|
||||||
|
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) == 0)
|
||||||
|
return sdlMode.refresh_rate;
|
||||||
|
|
||||||
|
if(myWindow != nullptr)
|
||||||
|
Logger::error("Could not retrieve current display mode");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
int FrameBufferSDL2::gameRefreshRate() const
|
||||||
|
{
|
||||||
|
if(myOSystem.hasConsole())
|
||||||
|
{
|
||||||
|
const string format = myOSystem.console().getFormatString();
|
||||||
|
const bool isNtsc = format == "NTSC" || format == "PAL60" || format == "SECAM60";
|
||||||
|
|
||||||
|
return isNtsc ? 60 : 50; // The code will take care of 59/49 Hz
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void FrameBufferSDL2::renderToScreen()
|
void FrameBufferSDL2::renderToScreen()
|
||||||
{
|
{
|
||||||
|
@ -419,10 +552,9 @@ void FrameBufferSDL2::renderToScreen()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void FrameBufferSDL2::setWindowIcon()
|
void FrameBufferSDL2::setWindowIcon()
|
||||||
{
|
{
|
||||||
ASSERT_MAIN_THREAD;
|
|
||||||
|
|
||||||
#if !defined(BSPF_MACOS) && !defined(RETRON77)
|
#if !defined(BSPF_MACOS) && !defined(RETRON77)
|
||||||
#include "stella_icon.hxx"
|
#include "stella_icon.hxx"
|
||||||
|
ASSERT_MAIN_THREAD;
|
||||||
|
|
||||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32,
|
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32,
|
||||||
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
|
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
|
||||||
|
|
|
@ -95,6 +95,21 @@ class FrameBufferSDL2 : public FrameBuffer
|
||||||
*/
|
*/
|
||||||
void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const override;
|
void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is called to query if the current window is not centered
|
||||||
|
or fullscreen.
|
||||||
|
|
||||||
|
@return True, if the current window is positioned
|
||||||
|
*/
|
||||||
|
bool isCurrentWindowPositioned() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is called to query the video hardware for position of
|
||||||
|
the current window
|
||||||
|
|
||||||
|
@return The position of the currently displayed window
|
||||||
|
*/
|
||||||
|
Common::Point getCurrentWindowPos() const override;
|
||||||
/**
|
/**
|
||||||
This method is called to query the video hardware for the index
|
This method is called to query the video hardware for the index
|
||||||
of the display the current window is displayed on
|
of the display the current window is displayed on
|
||||||
|
@ -102,12 +117,7 @@ class FrameBufferSDL2 : public FrameBuffer
|
||||||
@return the current display index or a negative value if no
|
@return the current display index or a negative value if no
|
||||||
window is displayed
|
window is displayed
|
||||||
*/
|
*/
|
||||||
Int32 getCurrentDisplayIndex() override;
|
Int32 getCurrentDisplayIndex() const override;
|
||||||
|
|
||||||
/**
|
|
||||||
This method is called to preserve the last current windowed position.
|
|
||||||
*/
|
|
||||||
void updateWindowedPos() override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Clear the frame buffer.
|
Clear the frame buffer.
|
||||||
|
@ -137,12 +147,12 @@ class FrameBufferSDL2 : public FrameBuffer
|
||||||
/**
|
/**
|
||||||
Transform from window to renderer coordinates, x direction
|
Transform from window to renderer coordinates, x direction
|
||||||
*/
|
*/
|
||||||
int scaleX(int x) const { return (x * myRenderW) / myWindowW; }
|
int scaleX(int x) const override { return (x * myRenderW) / myWindowW; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Transform from window to renderer coordinates, y direction
|
Transform from window to renderer coordinates, y direction
|
||||||
*/
|
*/
|
||||||
int scaleY(int y) const { return (y * myRenderH) / myWindowH; }
|
int scaleY(int y) const override { return (y * myRenderH) / myWindowH; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -171,6 +181,25 @@ class FrameBufferSDL2 : public FrameBuffer
|
||||||
*/
|
*/
|
||||||
bool setVideoMode(const string& title, const VideoMode& mode) override;
|
bool setVideoMode(const string& title, const VideoMode& mode) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if the display refresh rate should be adapted to game refresh rate in (real) fullscreen mode
|
||||||
|
|
||||||
|
@param displayIndex The display which should be checked
|
||||||
|
@param adaptedSdlMode The best matching mode if the refresh rate should be changed
|
||||||
|
|
||||||
|
@return True if the refresh rate should be changed
|
||||||
|
*/
|
||||||
|
bool adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a new renderer if required
|
||||||
|
|
||||||
|
@param force If true, force new renderer creation
|
||||||
|
|
||||||
|
@return False on any errors, else true
|
||||||
|
*/
|
||||||
|
bool createRenderer(bool force);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This method is called to create a surface with the given attributes.
|
This method is called to create a surface with the given attributes.
|
||||||
|
|
||||||
|
@ -223,6 +252,16 @@ class FrameBufferSDL2 : public FrameBuffer
|
||||||
*/
|
*/
|
||||||
void determineDimensions();
|
void determineDimensions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retrieve the current display's refresh rate, or 0 if no window
|
||||||
|
*/
|
||||||
|
int refreshRate() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retrieve the current game's refresh rate, or 60 if no game
|
||||||
|
*/
|
||||||
|
int gameRefreshRate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The SDL video buffer
|
// The SDL video buffer
|
||||||
SDL_Window* myWindow{nullptr};
|
SDL_Window* myWindow{nullptr};
|
||||||
|
|
|
@ -185,9 +185,35 @@ JoyMap::JoyMappingArray JoyMap::getEventMapping(const Event::Type event, const E
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string JoyMap::saveMapping(const EventMode mode) const
|
string JoyMap::saveMapping(const EventMode mode) const
|
||||||
{
|
{
|
||||||
|
using MapType = std::pair<JoyMapping, Event::Type>;
|
||||||
|
std::vector<MapType> sortedMap(myMap.begin(), myMap.end());
|
||||||
|
|
||||||
|
std::sort(sortedMap.begin(), sortedMap.end(),
|
||||||
|
[](const MapType& a, const MapType& b)
|
||||||
|
{
|
||||||
|
// Event::Type first
|
||||||
|
if(a.second != b.second)
|
||||||
|
return a.second < b.second;
|
||||||
|
|
||||||
|
if(a.first.button != b.first.button)
|
||||||
|
return a.first.button < b.first.button;
|
||||||
|
|
||||||
|
if(a.first.axis != b.first.axis)
|
||||||
|
return a.first.axis < b.first.axis;
|
||||||
|
|
||||||
|
if(a.first.adir != b.first.adir)
|
||||||
|
return a.first.adir < b.first.adir;
|
||||||
|
|
||||||
|
if(a.first.hat != b.first.hat)
|
||||||
|
return a.first.hat < b.first.hat;
|
||||||
|
|
||||||
|
return a.first.hdir < b.first.hdir;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
|
|
||||||
for (auto item : myMap)
|
for (auto item : sortedMap)
|
||||||
{
|
{
|
||||||
if (item.first.mode == mode)
|
if (item.first.mode == mode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define CONTROLLERMAP_HXX
|
#define CONTROLLERMAP_HXX
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "Event.hxx"
|
#include "Event.hxx"
|
||||||
#include "EventHandlerConstants.hxx"
|
#include "EventHandlerConstants.hxx"
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include "KeyMap.hxx"
|
#include "KeyMap.hxx"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void KeyMap::add(const Event::Type event, const Mapping& mapping)
|
void KeyMap::add(const Event::Type event, const Mapping& mapping)
|
||||||
|
@ -170,9 +171,26 @@ KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const Even
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string KeyMap::saveMapping(const EventMode mode) const
|
string KeyMap::saveMapping(const EventMode mode) const
|
||||||
{
|
{
|
||||||
|
using MapType = std::pair<Mapping, Event::Type>;
|
||||||
|
std::vector<MapType> sortedMap(myMap.begin(), myMap.end());
|
||||||
|
|
||||||
|
std::sort(sortedMap.begin(), sortedMap.end(),
|
||||||
|
[](const MapType& a, const MapType& b)
|
||||||
|
{
|
||||||
|
// Event::Type first
|
||||||
|
if(a.second != b.second)
|
||||||
|
return a.second < b.second;
|
||||||
|
|
||||||
|
if(a.first.key != b.first.key)
|
||||||
|
return a.first.key < b.first.key;
|
||||||
|
|
||||||
|
return a.first.mod < b.first.mod;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
|
|
||||||
for (auto item : myMap)
|
for (auto item : sortedMap)
|
||||||
{
|
{
|
||||||
if (item.first.mode == mode)
|
if (item.first.mode == mode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "Event.hxx"
|
#include "Event.hxx"
|
||||||
#include "EventHandlerConstants.hxx"
|
#include "EventHandlerConstants.hxx"
|
||||||
|
#include "StellaKeys.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This class handles keyboard mappings in Stella.
|
This class handles keyboard mappings in Stella.
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
*/
|
*/
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <class T, uInt32 CAPACITY = 100>
|
template <typename T, uInt32 CAPACITY = 100>
|
||||||
class LinkedObjectPool
|
class LinkedObjectPool
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -124,7 +124,7 @@ MouseControl::MouseControl(Console& console, const string& mode)
|
||||||
int m_range = 100;
|
int m_range = 100;
|
||||||
if(!(m_axis >> m_range))
|
if(!(m_axis >> m_range))
|
||||||
m_range = 100;
|
m_range = 100;
|
||||||
Paddles::setPaddleRange(m_range);
|
Paddles::setDigitalPaddleRange(m_range);
|
||||||
|
|
||||||
// If the mouse isn't used at all, we still need one item in the list
|
// If the mouse isn't used at all, we still need one item in the list
|
||||||
if(myModeList.size() == 0)
|
if(myModeList.size() == 0)
|
||||||
|
|
|
@ -250,9 +250,9 @@ void PhysicalJoystickHandler::setDefaultAction(int stick,
|
||||||
|
|
||||||
if(updateDefaults)
|
if(updateDefaults)
|
||||||
{
|
{
|
||||||
// if there is no existing mapping for the event or
|
// if there is no existing mapping for the event and
|
||||||
// the default mapping for the event is unused, set default key for event
|
// the default mapping for the event is unused, set default key for event
|
||||||
if(j->joyMap.getEventMapping(map.event, mode).size() == 0 ||
|
if(j->joyMap.getEventMapping(map.event, mode).size() == 0 &&
|
||||||
!j->joyMap.check(mode, map.button, map.axis, map.adir, map.hat, map.hdir))
|
!j->joyMap.check(mode, map.button, map.axis, map.adir, map.hat, map.hdir))
|
||||||
{
|
{
|
||||||
if (map.hat == JOY_CTRL_NONE)
|
if (map.hat == JOY_CTRL_NONE)
|
||||||
|
@ -687,7 +687,11 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
|
||||||
}
|
}
|
||||||
j->axisLastValue[axis] = value;
|
j->axisLastValue[axis] = value;
|
||||||
}
|
}
|
||||||
|
#ifdef GUI_SUPPORT
|
||||||
else if(myHandler.hasOverlay())
|
else if(myHandler.hasOverlay())
|
||||||
|
{
|
||||||
|
// A value change lower than Joystick::deadzone indicates analog input which is ignored
|
||||||
|
if((abs(j->axisLastValue[axis] - value) > Joystick::deadzone()))
|
||||||
{
|
{
|
||||||
// First, clamp the values to simulate digital input
|
// First, clamp the values to simulate digital input
|
||||||
// (the only thing that the underlying code understands)
|
// (the only thing that the underlying code understands)
|
||||||
|
@ -702,12 +706,13 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
|
||||||
// (only pass on the event if the state has changed)
|
// (only pass on the event if the state has changed)
|
||||||
if(value != j->axisLastValue[axis])
|
if(value != j->axisLastValue[axis])
|
||||||
{
|
{
|
||||||
#ifdef GUI_SUPPORT
|
|
||||||
myHandler.overlay().handleJoyAxisEvent(stick, JoyAxis(axis), convertAxisValue(value), button);
|
myHandler.overlay().handleJoyAxisEvent(stick, JoyAxis(axis), convertAxisValue(value), button);
|
||||||
#endif
|
|
||||||
|
}
|
||||||
|
}
|
||||||
j->axisLastValue[axis] = value;
|
j->axisLastValue[axis] = value;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,7 @@
|
||||||
|
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
#include "Console.hxx"
|
#include "Console.hxx"
|
||||||
#include "Settings.hxx"
|
|
||||||
#include "EventHandler.hxx"
|
#include "EventHandler.hxx"
|
||||||
#include "Sound.hxx"
|
|
||||||
#include "StateManager.hxx"
|
|
||||||
#include "StellaKeys.hxx"
|
|
||||||
#include "TIASurface.hxx"
|
|
||||||
#include "PNGLibrary.hxx"
|
|
||||||
#include "PKeyboardHandler.hxx"
|
#include "PKeyboardHandler.hxx"
|
||||||
|
|
||||||
#ifdef DEBUGGER_SUPPORT
|
#ifdef DEBUGGER_SUPPORT
|
||||||
|
@ -45,6 +39,7 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler&
|
||||||
myHandler(handler)
|
myHandler(handler)
|
||||||
{
|
{
|
||||||
Int32 version = myOSystem.settings().getInt("event_ver");
|
Int32 version = myOSystem.settings().getInt("event_ver");
|
||||||
|
bool updateDefaults = false;
|
||||||
|
|
||||||
// Compare if event list version has changed so that key maps became invalid
|
// Compare if event list version has changed so that key maps became invalid
|
||||||
if (version == Event::VERSION)
|
if (version == Event::VERSION)
|
||||||
|
@ -59,11 +54,37 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler&
|
||||||
myKeyMap.loadMapping(list, EventMode::kKeypadMode);
|
myKeyMap.loadMapping(list, EventMode::kKeypadMode);
|
||||||
list = myOSystem.settings().getString("keymap_ui");
|
list = myOSystem.settings().getString("keymap_ui");
|
||||||
myKeyMap.loadMapping(list, EventMode::kMenuMode);
|
myKeyMap.loadMapping(list, EventMode::kMenuMode);
|
||||||
|
updateDefaults = true;
|
||||||
}
|
}
|
||||||
myKeyMap.enableMod() = myOSystem.settings().getBool("modcombo");
|
myKeyMap.enableMod() = myOSystem.settings().getBool("modcombo");
|
||||||
|
|
||||||
setDefaultMapping(Event::NoType, EventMode::kEmulationMode, true);
|
setDefaultMapping(Event::NoType, EventMode::kEmulationMode, updateDefaults);
|
||||||
setDefaultMapping(Event::NoType, EventMode::kMenuMode, true);
|
setDefaultMapping(Event::NoType, EventMode::kMenuMode, updateDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool PhysicalKeyboardHandler::isMappingUsed(EventMode mode, const EventMapping& map) const
|
||||||
|
{
|
||||||
|
// Menu events can only interfere with
|
||||||
|
// - other menu events
|
||||||
|
if(mode == EventMode::kMenuMode)
|
||||||
|
return myKeyMap.check(EventMode::kMenuMode, map.key, map.mod);
|
||||||
|
|
||||||
|
// Controller events can interfere with
|
||||||
|
// - other events of the same controller
|
||||||
|
// - and common emulation events
|
||||||
|
if(mode != EventMode::kCommonMode)
|
||||||
|
return myKeyMap.check(mode, map.key, map.mod)
|
||||||
|
|| myKeyMap.check(EventMode::kCommonMode, map.key, map.mod);
|
||||||
|
|
||||||
|
// Common emulation events can interfere with
|
||||||
|
// - other common emulation events
|
||||||
|
// - and all controller events
|
||||||
|
return myKeyMap.check(EventMode::kCommonMode, map.key, map.mod)
|
||||||
|
|| myKeyMap.check(EventMode::kJoystickMode, map.key, map.mod)
|
||||||
|
|| myKeyMap.check(EventMode::kPaddlesMode, map.key, map.mod)
|
||||||
|
|| myKeyMap.check(EventMode::kKeypadMode, map.key, map.mod)
|
||||||
|
|| myKeyMap.check(EventMode::kCompuMateMode, map.key, map.mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -80,10 +101,10 @@ void PhysicalKeyboardHandler::setDefaultKey(EventMapping map, Event::Type event,
|
||||||
|
|
||||||
if (updateDefaults)
|
if (updateDefaults)
|
||||||
{
|
{
|
||||||
// if there is no existing mapping for the event or
|
// if there is no existing mapping for the event and
|
||||||
// the default mapping for the event is unused, set default key for event
|
// the default mapping for the event is unused, set default key for event
|
||||||
if (myKeyMap.getEventMapping(map.event, mode).size() == 0 ||
|
if (myKeyMap.getEventMapping(map.event, mode).size() == 0 &&
|
||||||
!myKeyMap.check(mode, map.key, map.mod))
|
!isMappingUsed(mode, map))
|
||||||
{
|
{
|
||||||
addMapping(map.event, mode, map.key, StellaMod(map.mod));
|
addMapping(map.event, mode, map.key, StellaMod(map.mod));
|
||||||
}
|
}
|
||||||
|
@ -395,6 +416,7 @@ void PhysicalKeyboardHandler::handleEvent(StellaKey key, StellaMod mod, bool pre
|
||||||
{
|
{
|
||||||
case EventHandlerState::EMULATION:
|
case EventHandlerState::EMULATION:
|
||||||
case EventHandlerState::PAUSE:
|
case EventHandlerState::PAUSE:
|
||||||
|
case EventHandlerState::PLAYBACK:
|
||||||
myHandler.handleEvent(myKeyMap.get(EventMode::kEmulationMode, key, mod), pressed, repeated);
|
myHandler.handleEvent(myKeyMap.get(EventMode::kEmulationMode, key, mod), pressed, repeated);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -424,7 +446,11 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
||||||
{Event::LoadState, KBDK_F11},
|
{Event::LoadState, KBDK_F11},
|
||||||
{Event::LoadAllStates, KBDK_F11, MOD3},
|
{Event::LoadAllStates, KBDK_F11, MOD3},
|
||||||
{Event::TakeSnapshot, KBDK_F12},
|
{Event::TakeSnapshot, KBDK_F12},
|
||||||
|
#ifdef BSPF_MACOS
|
||||||
|
{Event::TogglePauseMode, KBDK_P, KBDM_SHIFT | MOD3},
|
||||||
|
#else
|
||||||
{Event::TogglePauseMode, KBDK_PAUSE},
|
{Event::TogglePauseMode, KBDK_PAUSE},
|
||||||
|
#endif
|
||||||
{Event::OptionsMenuMode, KBDK_TAB},
|
{Event::OptionsMenuMode, KBDK_TAB},
|
||||||
{Event::CmdMenuMode, KBDK_BACKSLASH},
|
{Event::CmdMenuMode, KBDK_BACKSLASH},
|
||||||
{Event::TimeMachineMode, KBDK_T, KBDM_SHIFT},
|
{Event::TimeMachineMode, KBDK_T, KBDM_SHIFT},
|
||||||
|
@ -436,45 +462,83 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
||||||
{Event::Quit, KBDK_Q, KBDM_CTRL},
|
{Event::Quit, KBDK_Q, KBDM_CTRL},
|
||||||
#endif
|
#endif
|
||||||
{Event::ReloadConsole, KBDK_R, KBDM_CTRL},
|
{Event::ReloadConsole, KBDK_R, KBDM_CTRL},
|
||||||
|
{Event::PreviousMultiCartRom, KBDK_R, KBDM_SHIFT | KBDM_CTRL},
|
||||||
|
|
||||||
{Event::VidmodeDecrease, KBDK_MINUS, MOD3},
|
{Event::VidmodeDecrease, KBDK_MINUS, MOD3},
|
||||||
{Event::VidmodeIncrease, KBDK_EQUALS, MOD3},
|
{Event::VidmodeIncrease, KBDK_EQUALS, MOD3},
|
||||||
{Event::VCenterDecrease, KBDK_PAGEUP, MOD3},
|
{Event::VCenterDecrease, KBDK_PAGEUP, MOD3},
|
||||||
{Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3},
|
{Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3},
|
||||||
{Event::ScanlineAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3},
|
{Event::VSizeAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3},
|
||||||
{Event::ScanlineAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3},
|
{Event::VSizeAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3},
|
||||||
{Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3},
|
{Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3},
|
||||||
{Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3},
|
{Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3},
|
||||||
{Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL},
|
{Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL},
|
||||||
|
|
||||||
{Event::ToggleFullScreen, KBDK_RETURN, MOD3},
|
{Event::ToggleFullScreen, KBDK_RETURN, MOD3},
|
||||||
|
{Event::ToggleAdaptRefresh, KBDK_R, MOD3},
|
||||||
{Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT},
|
{Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT},
|
||||||
{Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT},
|
{Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT},
|
||||||
{Event::VidmodeStd, KBDK_1, MOD3},
|
//{Event::VidmodeStd, KBDK_1, MOD3},
|
||||||
{Event::VidmodeRGB, KBDK_2, MOD3},
|
//{Event::VidmodeRGB, KBDK_2, MOD3},
|
||||||
{Event::VidmodeSVideo, KBDK_3, MOD3},
|
//{Event::VidmodeSVideo, KBDK_3, MOD3},
|
||||||
{Event::VidModeComposite, KBDK_4, MOD3},
|
//{Event::VidModeComposite, KBDK_4, MOD3},
|
||||||
{Event::VidModeBad, KBDK_5, MOD3},
|
//{Event::VidModeBad, KBDK_5, MOD3},
|
||||||
{Event::VidModeCustom, KBDK_6, MOD3},
|
//{Event::VidModeCustom, KBDK_6, MOD3},
|
||||||
{Event::PreviousAttribute, KBDK_7, KBDM_SHIFT | MOD3},
|
{Event::PreviousVideoMode, KBDK_1, KBDM_SHIFT | MOD3},
|
||||||
{Event::NextAttribute, KBDK_7, MOD3},
|
{Event::NextVideoMode, KBDK_1, MOD3},
|
||||||
{Event::DecreaseAttribute, KBDK_8, KBDM_SHIFT | MOD3},
|
{Event::PreviousAttribute, KBDK_2, KBDM_SHIFT | MOD3},
|
||||||
{Event::IncreaseAttribute, KBDK_8, MOD3},
|
{Event::NextAttribute, KBDK_2, MOD3},
|
||||||
{Event::PhosphorDecrease, KBDK_9, KBDM_SHIFT | MOD3},
|
{Event::DecreaseAttribute, KBDK_3, KBDM_SHIFT | MOD3},
|
||||||
{Event::PhosphorIncrease, KBDK_9, MOD3},
|
{Event::IncreaseAttribute, KBDK_3, MOD3},
|
||||||
|
{Event::PhosphorDecrease, KBDK_4, KBDM_SHIFT | MOD3},
|
||||||
|
{Event::PhosphorIncrease, KBDK_4, MOD3},
|
||||||
{Event::TogglePhosphor, KBDK_P, MOD3},
|
{Event::TogglePhosphor, KBDK_P, MOD3},
|
||||||
{Event::ScanlinesDecrease, KBDK_0, KBDM_SHIFT | MOD3},
|
{Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3},
|
||||||
{Event::ScanlinesIncrease, KBDK_0, MOD3},
|
{Event::ScanlinesIncrease, KBDK_5, MOD3},
|
||||||
|
{Event::PreviousPaletteAttribute, KBDK_9, KBDM_SHIFT | MOD3},
|
||||||
|
{Event::NextPaletteAttribute, KBDK_9, MOD3},
|
||||||
|
{Event::PaletteAttributeDecrease, KBDK_0, KBDM_SHIFT | MOD3},
|
||||||
|
{Event::PaletteAttributeIncrease, KBDK_0, MOD3},
|
||||||
{Event::ToggleColorLoss, KBDK_L, KBDM_CTRL},
|
{Event::ToggleColorLoss, KBDK_L, KBDM_CTRL},
|
||||||
{Event::TogglePalette, KBDK_P, KBDM_CTRL},
|
{Event::PaletteDecrease, KBDK_P, KBDM_SHIFT | KBDM_CTRL},
|
||||||
|
{Event::PaletteIncrease, KBDK_P, KBDM_CTRL},
|
||||||
|
|
||||||
|
#ifndef BSPF_MACOS
|
||||||
|
{Event::PreviousSetting, KBDK_END},
|
||||||
|
{Event::NextSetting, KBDK_HOME},
|
||||||
|
{Event::PreviousSettingGroup, KBDK_END, KBDM_CTRL},
|
||||||
|
{Event::NextSettingGroup, KBDK_HOME, KBDM_CTRL},
|
||||||
|
#else
|
||||||
|
// HOME & END keys are swapped on Mac keyboards
|
||||||
|
{Event::PreviousSetting, KBDK_HOME},
|
||||||
|
{Event::NextSetting, KBDK_END},
|
||||||
|
{Event::PreviousSettingGroup, KBDK_HOME, KBDM_CTRL},
|
||||||
|
{Event::NextSettingGroup, KBDK_END, KBDM_CTRL},
|
||||||
|
#endif
|
||||||
|
{Event::PreviousSetting, KBDK_KP_1},
|
||||||
|
{Event::NextSetting, KBDK_KP_7},
|
||||||
|
{Event::PreviousSettingGroup, KBDK_KP_1, KBDM_CTRL},
|
||||||
|
{Event::NextSettingGroup, KBDK_KP_7, KBDM_CTRL},
|
||||||
|
{Event::SettingDecrease, KBDK_PAGEDOWN},
|
||||||
|
{Event::SettingDecrease, KBDK_KP_3, KBDM_CTRL},
|
||||||
|
{Event::SettingIncrease, KBDK_PAGEUP},
|
||||||
|
{Event::SettingIncrease, KBDK_KP_9, KBDM_CTRL},
|
||||||
|
|
||||||
{Event::ToggleInter, KBDK_I, KBDM_CTRL},
|
{Event::ToggleInter, KBDK_I, KBDM_CTRL},
|
||||||
|
{Event::DecreaseSpeed, KBDK_S, KBDM_SHIFT | KBDM_CTRL},
|
||||||
|
{Event::IncreaseSpeed, KBDK_S, KBDM_CTRL },
|
||||||
|
{Event::ToggleTurbo, KBDK_T, KBDM_CTRL},
|
||||||
{Event::ToggleJitter, KBDK_J, MOD3},
|
{Event::ToggleJitter, KBDK_J, MOD3},
|
||||||
{Event::ToggleFrameStats, KBDK_L, MOD3},
|
{Event::ToggleFrameStats, KBDK_L, MOD3},
|
||||||
{Event::ToggleTimeMachine, KBDK_T, MOD3},
|
{Event::ToggleTimeMachine, KBDK_T, MOD3},
|
||||||
|
|
||||||
#ifdef PNG_SUPPORT
|
#ifdef PNG_SUPPORT
|
||||||
{Event::ToggleContSnapshots, KBDK_S, MOD3},
|
{Event::ToggleContSnapshots, KBDK_S, MOD3},
|
||||||
{Event::ToggleContSnapshotsFrame, KBDK_S, KBDM_SHIFT | MOD3},
|
{Event::ToggleContSnapshotsFrame, KBDK_S, KBDM_SHIFT | MOD3},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
{Event::DecreaseAutoFire, KBDK_A, KBDM_SHIFT | KBDM_CTRL},
|
||||||
|
{Event::IncreaseAutoFire, KBDK_A, KBDM_CTRL },
|
||||||
{Event::HandleMouseControl, KBDK_0, KBDM_CTRL},
|
{Event::HandleMouseControl, KBDK_0, KBDM_CTRL},
|
||||||
{Event::ToggleGrabMouse, KBDK_G, KBDM_CTRL},
|
{Event::ToggleGrabMouse, KBDK_G, KBDM_CTRL},
|
||||||
{Event::ToggleSAPortOrder, KBDK_1, KBDM_CTRL},
|
{Event::ToggleSAPortOrder, KBDK_1, KBDM_CTRL},
|
||||||
|
@ -506,6 +570,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
||||||
{Event::Unwind10Menu, KBDK_RIGHT, KBDM_SHIFT | MOD3},
|
{Event::Unwind10Menu, KBDK_RIGHT, KBDM_SHIFT | MOD3},
|
||||||
{Event::UnwindAllMenu, KBDK_UP, MOD3},
|
{Event::UnwindAllMenu, KBDK_UP, MOD3},
|
||||||
{Event::HighScoresMenuMode, KBDK_INSERT},
|
{Event::HighScoresMenuMode, KBDK_INSERT},
|
||||||
|
{Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT},
|
||||||
|
|
||||||
#if defined(RETRON77)
|
#if defined(RETRON77)
|
||||||
{Event::ConsoleColorToggle, KBDK_F4}, // back ("COLOR","B/W")
|
{Event::ConsoleColorToggle, KBDK_F4}, // back ("COLOR","B/W")
|
||||||
|
|
|
@ -87,6 +87,9 @@ class PhysicalKeyboardHandler
|
||||||
};
|
};
|
||||||
using EventMappingArray = std::vector<EventMapping>;
|
using EventMappingArray = std::vector<EventMapping>;
|
||||||
|
|
||||||
|
// Checks if the given mapping is used by any event mode
|
||||||
|
bool isMappingUsed(EventMode mode, const EventMapping& map) const;
|
||||||
|
|
||||||
void setDefaultKey(EventMapping map, Event::Type event = Event::NoType,
|
void setDefaultKey(EventMapping map, Event::Type event = Event::NoType,
|
||||||
EventMode mode = EventMode::kEmulationMode, bool updateDefaults = false);
|
EventMode mode = EventMode::kEmulationMode, bool updateDefaults = false);
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "TIASurface.hxx"
|
#include "TIASurface.hxx"
|
||||||
#include "Version.hxx"
|
#include "Version.hxx"
|
||||||
#include "PNGLibrary.hxx"
|
#include "PNGLibrary.hxx"
|
||||||
|
#include "Rect.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
PNGLibrary::PNGLibrary(OSystem& osystem)
|
PNGLibrary::PNGLibrary(OSystem& osystem)
|
||||||
|
@ -51,7 +52,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
||||||
throw runtime_error(s);
|
throw runtime_error(s);
|
||||||
};
|
};
|
||||||
|
|
||||||
ifstream in(filename, std::ios_base::binary);
|
std::ifstream in(filename, std::ios_base::binary);
|
||||||
if(!in.is_open())
|
if(!in.is_open())
|
||||||
loadImageERROR("No snapshot found");
|
loadImageERROR("No snapshot found");
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
||||||
}
|
}
|
||||||
else if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
else if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
{
|
{
|
||||||
loadImageERROR("Greyscale PNG images not supported");
|
png_set_gray_to_rgb(png_ptr);
|
||||||
}
|
}
|
||||||
else if(color_type == PNG_COLOR_TYPE_PALETTE)
|
else if(color_type == PNG_COLOR_TYPE_PALETTE)
|
||||||
{
|
{
|
||||||
|
@ -124,12 +125,18 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
|
void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
|
||||||
{
|
{
|
||||||
ofstream out(filename, std::ios_base::binary);
|
std::ofstream out(filename, std::ios_base::binary);
|
||||||
if(!out.is_open())
|
if(!out.is_open())
|
||||||
throw runtime_error("ERROR: Couldn't create snapshot file");
|
throw runtime_error("ERROR: Couldn't create snapshot file");
|
||||||
|
|
||||||
const FrameBuffer& fb = myOSystem.frameBuffer();
|
const FrameBuffer& fb = myOSystem.frameBuffer();
|
||||||
const Common::Rect& rect = fb.imageRect();
|
|
||||||
|
const Common::Rect& rectUnscaled = fb.imageRect();
|
||||||
|
const Common::Rect rect(
|
||||||
|
Common::Point(fb.scaleX(rectUnscaled.x()), fb.scaleY(rectUnscaled.y())),
|
||||||
|
fb.scaleX(rectUnscaled.w()), fb.scaleY(rectUnscaled.h())
|
||||||
|
);
|
||||||
|
|
||||||
png_uint_32 width = rect.w(), height = rect.h();
|
png_uint_32 width = rect.w(), height = rect.h();
|
||||||
|
|
||||||
// Get framebuffer pixel data (we get ABGR format)
|
// Get framebuffer pixel data (we get ABGR format)
|
||||||
|
@ -149,7 +156,7 @@ void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
|
||||||
void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
|
void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
|
||||||
const Common::Rect& rect, const VariantList& comments)
|
const Common::Rect& rect, const VariantList& comments)
|
||||||
{
|
{
|
||||||
ofstream out(filename, std::ios_base::binary);
|
std::ofstream out(filename, std::ios_base::binary);
|
||||||
if(!out.is_open())
|
if(!out.is_open())
|
||||||
throw runtime_error("ERROR: Couldn't create snapshot file");
|
throw runtime_error("ERROR: Couldn't create snapshot file");
|
||||||
|
|
||||||
|
@ -175,7 +182,7 @@ void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::saveImageToDisk(ofstream& out, const vector<png_bytep>& rows,
|
void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
|
||||||
png_uint_32 width, png_uint_32 height, const VariantList& comments)
|
png_uint_32 width, png_uint_32 height, const VariantList& comments)
|
||||||
{
|
{
|
||||||
png_structp png_ptr = nullptr;
|
png_structp png_ptr = nullptr;
|
||||||
|
@ -290,7 +297,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
|
||||||
// Figure out the correct snapshot name
|
// Figure out the correct snapshot name
|
||||||
string filename;
|
string filename;
|
||||||
bool showmessage = number == 0;
|
bool showmessage = number == 0;
|
||||||
string sspath = myOSystem.snapshotSaveDir() +
|
string sspath = myOSystem.snapshotSaveDir().getPath() +
|
||||||
(myOSystem.settings().getString("snapname") != "int" ?
|
(myOSystem.settings().getString("snapname") != "int" ?
|
||||||
myOSystem.romFile().getNameWithExt("")
|
myOSystem.romFile().getNameWithExt("")
|
||||||
: myOSystem.console().properties().get(PropType::Cart_Name));
|
: myOSystem.console().properties().get(PropType::Cart_Name));
|
||||||
|
@ -449,21 +456,21 @@ void PNGLibrary::writeComments(png_structp png_ptr, png_infop info_ptr,
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size)
|
void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size)
|
||||||
{
|
{
|
||||||
(static_cast<ifstream*>(png_get_io_ptr(ctx)))->read(
|
(static_cast<std::ifstream*>(png_get_io_ptr(ctx)))->read(
|
||||||
reinterpret_cast<char *>(area), size);
|
reinterpret_cast<char *>(area), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
|
void PNGLibrary::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
|
||||||
{
|
{
|
||||||
(static_cast<ofstream*>(png_get_io_ptr(ctx)))->write(
|
(static_cast<std::ofstream*>(png_get_io_ptr(ctx)))->write(
|
||||||
reinterpret_cast<const char *>(area), size);
|
reinterpret_cast<const char *>(area), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::png_io_flush(png_structp ctx)
|
void PNGLibrary::png_io_flush(png_structp ctx)
|
||||||
{
|
{
|
||||||
(static_cast<ofstream*>(png_get_io_ptr(ctx)))->flush();
|
(static_cast<std::ofstream*>(png_get_io_ptr(ctx)))->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -165,7 +165,7 @@ class PNGLibrary
|
||||||
@param height The height of the PNG image
|
@param height The height of the PNG image
|
||||||
@param comments The text comments to add to the PNG image
|
@param comments The text comments to add to the PNG image
|
||||||
*/
|
*/
|
||||||
void saveImageToDisk(ofstream& out, const vector<png_bytep>& rows,
|
void saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
|
||||||
png_uint_32 width, png_uint_32 height,
|
png_uint_32 width, png_uint_32 height,
|
||||||
const VariantList& comments);
|
const VariantList& comments);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,723 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// 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-2020 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.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "Console.hxx"
|
||||||
|
#include "FrameBuffer.hxx"
|
||||||
|
|
||||||
|
#include "PaletteHandler.hxx"
|
||||||
|
|
||||||
|
PaletteHandler::PaletteHandler(OSystem& system)
|
||||||
|
: myOSystem(system)
|
||||||
|
{
|
||||||
|
// Load user-defined palette for this ROM
|
||||||
|
loadUserPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteHandler::PaletteType PaletteHandler::toPaletteType(const string& name) const
|
||||||
|
{
|
||||||
|
if(name == SETTING_Z26)
|
||||||
|
return PaletteType::Z26;
|
||||||
|
|
||||||
|
if(name == SETTING_USER && myUserPaletteDefined)
|
||||||
|
return PaletteType::User;
|
||||||
|
|
||||||
|
if(name == SETTING_CUSTOM)
|
||||||
|
return PaletteType::Custom;
|
||||||
|
|
||||||
|
return PaletteType::Standard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
string PaletteHandler::toPaletteName(PaletteType type) const
|
||||||
|
{
|
||||||
|
const string SETTING_NAMES[int(PaletteType::NumTypes)] = {
|
||||||
|
SETTING_STANDARD, SETTING_Z26, SETTING_USER, SETTING_CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
return SETTING_NAMES[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::cyclePalette(int direction)
|
||||||
|
{
|
||||||
|
const string MESSAGES[PaletteType::NumTypes] = {
|
||||||
|
"Standard Stella", "Z26", "User-defined", "Custom"
|
||||||
|
};
|
||||||
|
int type = toPaletteType(myOSystem.settings().getString("palette"));
|
||||||
|
|
||||||
|
do {
|
||||||
|
type = BSPF::clampw(type + direction, int(PaletteType::MinType), int(PaletteType::MaxType));
|
||||||
|
} while(type == PaletteType::User && !myUserPaletteDefined);
|
||||||
|
|
||||||
|
const string palette = toPaletteName(PaletteType(type));
|
||||||
|
const string message = MESSAGES[type] + " palette";
|
||||||
|
|
||||||
|
myOSystem.frameBuffer().showMessage(message);
|
||||||
|
|
||||||
|
setPalette(palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::showAdjustableMessage()
|
||||||
|
{
|
||||||
|
const bool isPhaseShift = myAdjustables[myCurrentAdjustable].value == nullptr;
|
||||||
|
ostringstream msg, buf;
|
||||||
|
|
||||||
|
msg << "Palette " << myAdjustables[myCurrentAdjustable].name;
|
||||||
|
if(isPhaseShift)
|
||||||
|
{
|
||||||
|
const ConsoleTiming timing = myOSystem.console().timing();
|
||||||
|
const bool isNTSC = timing == ConsoleTiming::ntsc;
|
||||||
|
const float value =
|
||||||
|
myOSystem.console().timing() == ConsoleTiming::pal ? myPhasePAL : myPhaseNTSC;
|
||||||
|
buf << std::fixed << std::setprecision(1) << value << DEGREE;
|
||||||
|
myOSystem.frameBuffer().showMessage(
|
||||||
|
"Palette phase shift", buf.str(), value,
|
||||||
|
(isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) - MAX_SHIFT,
|
||||||
|
(isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) + MAX_SHIFT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int value = scaleTo100(*myAdjustables[myCurrentAdjustable].value);
|
||||||
|
buf << value << "%";
|
||||||
|
myOSystem.frameBuffer().showMessage(
|
||||||
|
msg.str(), buf.str(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::cycleAdjustable(int direction)
|
||||||
|
{
|
||||||
|
const bool isCustomPalette = SETTING_CUSTOM == myOSystem.settings().getString("palette");
|
||||||
|
bool isPhaseShift;
|
||||||
|
|
||||||
|
do {
|
||||||
|
myCurrentAdjustable = BSPF::clampw(int(myCurrentAdjustable + direction), 0, NUM_ADJUSTABLES - 1);
|
||||||
|
isPhaseShift = myAdjustables[myCurrentAdjustable].value == nullptr;
|
||||||
|
// skip phase shift when 'Custom' palette is not selected
|
||||||
|
if(!direction && isPhaseShift && !isCustomPalette)
|
||||||
|
myCurrentAdjustable++;
|
||||||
|
} while(isPhaseShift && !isCustomPalette);
|
||||||
|
|
||||||
|
showAdjustableMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::changeAdjustable(int adjustable, int direction)
|
||||||
|
{
|
||||||
|
const bool isCustomPalette = SETTING_CUSTOM == myOSystem.settings().getString("palette");
|
||||||
|
const bool isPhaseShift = myAdjustables[adjustable].value == nullptr;
|
||||||
|
|
||||||
|
myCurrentAdjustable = adjustable;
|
||||||
|
if(isPhaseShift && !isCustomPalette)
|
||||||
|
myCurrentAdjustable++;
|
||||||
|
|
||||||
|
changeCurrentAdjustable(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::changeCurrentAdjustable(int direction)
|
||||||
|
{
|
||||||
|
if(myAdjustables[myCurrentAdjustable].value == nullptr)
|
||||||
|
changeColorPhaseShift(direction);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int newVal = scaleTo100(*myAdjustables[myCurrentAdjustable].value);
|
||||||
|
|
||||||
|
newVal = BSPF::clamp(newVal + direction * 1, 0, 100);
|
||||||
|
|
||||||
|
*myAdjustables[myCurrentAdjustable].value = scaleFrom100(newVal);
|
||||||
|
|
||||||
|
showAdjustableMessage();
|
||||||
|
setPalette();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::changeColorPhaseShift(int direction)
|
||||||
|
{
|
||||||
|
const ConsoleTiming timing = myOSystem.console().timing();
|
||||||
|
|
||||||
|
// SECAM is not supported
|
||||||
|
if(timing != ConsoleTiming::secam)
|
||||||
|
{
|
||||||
|
const bool isNTSC = timing == ConsoleTiming::ntsc;
|
||||||
|
const float shift = isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT;
|
||||||
|
float newPhase = isNTSC ? myPhaseNTSC : myPhasePAL;
|
||||||
|
|
||||||
|
newPhase = BSPF::clamp(newPhase + direction * 0.3F, shift - MAX_SHIFT, shift + MAX_SHIFT);
|
||||||
|
|
||||||
|
if(isNTSC)
|
||||||
|
myPhaseNTSC = newPhase;
|
||||||
|
else
|
||||||
|
myPhasePAL = newPhase;
|
||||||
|
|
||||||
|
generateCustomPalette(timing);
|
||||||
|
setPalette(SETTING_CUSTOM);
|
||||||
|
|
||||||
|
showAdjustableMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::loadConfig(const Settings& settings)
|
||||||
|
{
|
||||||
|
// Load adjustables
|
||||||
|
myPhaseNTSC = BSPF::clamp(settings.getFloat("pal.phase_ntsc"),
|
||||||
|
DEF_NTSC_SHIFT - MAX_SHIFT, DEF_NTSC_SHIFT + MAX_SHIFT);
|
||||||
|
myPhasePAL = BSPF::clamp(settings.getFloat("pal.phase_pal"),
|
||||||
|
DEF_PAL_SHIFT - MAX_SHIFT, DEF_PAL_SHIFT + MAX_SHIFT);
|
||||||
|
|
||||||
|
myHue = BSPF::clamp(settings.getFloat("pal.hue"), -1.0F, 1.0F);
|
||||||
|
mySaturation = BSPF::clamp(settings.getFloat("pal.saturation"), -1.0F, 1.0F);
|
||||||
|
myContrast = BSPF::clamp(settings.getFloat("pal.contrast"), -1.0F, 1.0F);
|
||||||
|
myBrightness = BSPF::clamp(settings.getFloat("pal.brightness"), -1.0F, 1.0F);
|
||||||
|
myGamma = BSPF::clamp(settings.getFloat("pal.gamma"), -1.0F, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::saveConfig(Settings& settings) const
|
||||||
|
{
|
||||||
|
// Save adjustables
|
||||||
|
settings.setValue("pal.phase_ntsc", myPhaseNTSC);
|
||||||
|
settings.setValue("pal.phase_pal", myPhasePAL);
|
||||||
|
|
||||||
|
settings.setValue("pal.hue", myHue);
|
||||||
|
settings.setValue("pal.saturation", mySaturation);
|
||||||
|
settings.setValue("pal.contrast", myContrast);
|
||||||
|
settings.setValue("pal.brightness", myBrightness);
|
||||||
|
settings.setValue("pal.gamma", myGamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::setAdjustables(const Adjustable& adjustable)
|
||||||
|
{
|
||||||
|
myPhaseNTSC = adjustable.phaseNtsc / 10.F;
|
||||||
|
myPhasePAL = adjustable.phasePal / 10.F;
|
||||||
|
|
||||||
|
myHue = scaleFrom100(adjustable.hue);
|
||||||
|
mySaturation = scaleFrom100(adjustable.saturation);
|
||||||
|
myContrast = scaleFrom100(adjustable.contrast);
|
||||||
|
myBrightness = scaleFrom100(adjustable.brightness);
|
||||||
|
myGamma = scaleFrom100(adjustable.gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::getAdjustables(Adjustable& adjustable) const
|
||||||
|
{
|
||||||
|
adjustable.phaseNtsc = myPhaseNTSC * 10.F;
|
||||||
|
adjustable.phasePal = myPhasePAL * 10.F;
|
||||||
|
|
||||||
|
adjustable.hue = scaleTo100(myHue);
|
||||||
|
adjustable.saturation = scaleTo100(mySaturation);
|
||||||
|
adjustable.contrast = scaleTo100(myContrast);
|
||||||
|
adjustable.brightness = scaleTo100(myBrightness);
|
||||||
|
adjustable.gamma = scaleTo100(myGamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::setPalette(const string& name)
|
||||||
|
{
|
||||||
|
myOSystem.settings().setValue("palette", name);
|
||||||
|
|
||||||
|
setPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::setPalette()
|
||||||
|
{
|
||||||
|
if(myOSystem.hasConsole())
|
||||||
|
{
|
||||||
|
const string& name = myOSystem.settings().getString("palette");
|
||||||
|
|
||||||
|
// Look at all the palettes, since we don't know which one is
|
||||||
|
// currently active
|
||||||
|
static constexpr BSPF::array2D<const PaletteArray*, PaletteType::NumTypes, int(ConsoleTiming::numTimings)> palettes = {{
|
||||||
|
{ &ourNTSCPalette, &ourPALPalette, &ourSECAMPalette },
|
||||||
|
{ &ourNTSCPaletteZ26, &ourPALPaletteZ26, &ourSECAMPaletteZ26 },
|
||||||
|
{ &ourUserNTSCPalette, &ourUserPALPalette, &ourUserSECAMPalette },
|
||||||
|
{ &ourCustomNTSCPalette, &ourCustomPALPalette, &ourSECAMPalette }
|
||||||
|
}};
|
||||||
|
// See which format we should be using
|
||||||
|
const ConsoleTiming timing = myOSystem.console().timing();
|
||||||
|
const PaletteType paletteType = toPaletteType(name);
|
||||||
|
// Now consider the current display format
|
||||||
|
const PaletteArray* palette = palettes[paletteType][int(timing)];
|
||||||
|
|
||||||
|
if(paletteType == PaletteType::Custom)
|
||||||
|
generateCustomPalette(timing);
|
||||||
|
|
||||||
|
myOSystem.frameBuffer().setTIAPalette(adjustedPalette(*palette));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteArray PaletteHandler::adjustedPalette(const PaletteArray& palette)
|
||||||
|
{
|
||||||
|
PaletteArray destPalette;
|
||||||
|
// Constants for saturation and gray scale calculation
|
||||||
|
constexpr float PR = .2989F;
|
||||||
|
constexpr float PG = .5870F;
|
||||||
|
constexpr float PB = .1140F;
|
||||||
|
// Generate adjust table
|
||||||
|
constexpr int ADJUST_SIZE = 256;
|
||||||
|
constexpr int RGB_UNIT = 1 << 8;
|
||||||
|
constexpr float RGB_OFFSET = 0.5F;
|
||||||
|
const float hue = myHue;
|
||||||
|
const float brightness = myBrightness * (0.5F * RGB_UNIT) + RGB_OFFSET;
|
||||||
|
const float contrast = myContrast * (0.5F * RGB_UNIT) + RGB_UNIT;
|
||||||
|
const float saturation = mySaturation + 1;
|
||||||
|
const float gamma = 1.1333F - myGamma * 0.5F;
|
||||||
|
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
|
||||||
|
constexpr float toFloat = 1.F / (ADJUST_SIZE - 1);
|
||||||
|
std::array<float, ADJUST_SIZE> adjust;
|
||||||
|
|
||||||
|
for(int i = 0; i < ADJUST_SIZE; i++)
|
||||||
|
adjust[i] = powf(i * toFloat, gamma) * contrast + brightness;
|
||||||
|
|
||||||
|
// Transform original palette into destination palette
|
||||||
|
for(size_t i = 0; i < destPalette.size(); i += 2)
|
||||||
|
{
|
||||||
|
const uInt32 pixel = palette[i];
|
||||||
|
int r = (pixel >> 16) & 0xff;
|
||||||
|
int g = (pixel >> 8) & 0xff;
|
||||||
|
int b = (pixel >> 0) & 0xff;
|
||||||
|
|
||||||
|
// adjust hue (different for NTSC and PAL?) and saturation
|
||||||
|
adjustHueSaturation(r, g, b, hue, saturation);
|
||||||
|
|
||||||
|
// adjust contrast, brightness, gamma
|
||||||
|
r = adjust[r];
|
||||||
|
g = adjust[g];
|
||||||
|
b = adjust[b];
|
||||||
|
|
||||||
|
r = BSPF::clamp(r, 0, 255);
|
||||||
|
g = BSPF::clamp(g, 0, 255);
|
||||||
|
b = BSPF::clamp(b, 0, 255);
|
||||||
|
|
||||||
|
destPalette[i] = (r << 16) + (g << 8) + b;
|
||||||
|
|
||||||
|
// Fill the odd numbered palette entries with gray values (calculated
|
||||||
|
// using the standard RGB -> grayscale conversion formula)
|
||||||
|
// Used for PAL color-loss data and 'greying out' the frame in the debugger.
|
||||||
|
const uInt8 lum = static_cast<uInt8>((r * PR) + (g * PG) + (b * PB));
|
||||||
|
|
||||||
|
destPalette[i + 1] = (lum << 16) + (lum << 8) + lum;
|
||||||
|
}
|
||||||
|
return destPalette;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::loadUserPalette()
|
||||||
|
{
|
||||||
|
if (!myOSystem.checkUserPalette(true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ByteBuffer in;
|
||||||
|
try { myOSystem.paletteFile().read(in); }
|
||||||
|
catch(...) { return; }
|
||||||
|
|
||||||
|
uInt8* pixbuf = in.get();
|
||||||
|
for(int i = 0; i < 128; i++, pixbuf += 3) // NTSC palette
|
||||||
|
{
|
||||||
|
const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
|
||||||
|
ourUserNTSCPalette[(i<<1)] = pixel;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < 128; i++, pixbuf += 3) // PAL palette
|
||||||
|
{
|
||||||
|
const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
|
||||||
|
ourUserPALPalette[(i<<1)] = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uInt32, 16> secam; // All 8 24-bit pixels, plus 8 colorloss pixels
|
||||||
|
for(int i = 0; i < 8; i++, pixbuf += 3) // SECAM palette
|
||||||
|
{
|
||||||
|
const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
|
||||||
|
secam[(i<<1)] = pixel;
|
||||||
|
secam[(i<<1)+1] = 0;
|
||||||
|
}
|
||||||
|
uInt32* ptr = ourUserSECAMPalette.data();
|
||||||
|
for(int i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
const uInt32* s = secam.data();
|
||||||
|
for(int j = 0; j < 16; ++j)
|
||||||
|
*ptr++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
myUserPaletteDefined = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::generateCustomPalette(ConsoleTiming timing)
|
||||||
|
{
|
||||||
|
constexpr int NUM_CHROMA = 16;
|
||||||
|
constexpr int NUM_LUMA = 8;
|
||||||
|
constexpr float SATURATION = 0.25F; // default saturation
|
||||||
|
|
||||||
|
float color[NUM_CHROMA][2] = {{0.0F}};
|
||||||
|
|
||||||
|
if(timing == ConsoleTiming::ntsc)
|
||||||
|
{
|
||||||
|
// YIQ is YUV shifted by 33°
|
||||||
|
constexpr float offset = 33 * BSPF::PI_f / 180;
|
||||||
|
const float shift = myPhaseNTSC * BSPF::PI_f / 180;
|
||||||
|
|
||||||
|
// color 0 is grayscale
|
||||||
|
for(int chroma = 1; chroma < NUM_CHROMA; chroma++)
|
||||||
|
{
|
||||||
|
color[chroma][0] = SATURATION * sinf(offset + shift * (chroma - 1));
|
||||||
|
color[chroma][1] = SATURATION * cosf(offset + shift * (chroma - 1) - BSPF::PI_f);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int chroma = 0; chroma < NUM_CHROMA; chroma++)
|
||||||
|
{
|
||||||
|
const float I = color[chroma][0];
|
||||||
|
const float Q = color[chroma][1];
|
||||||
|
|
||||||
|
for(int luma = 0; luma < NUM_LUMA; luma++)
|
||||||
|
{
|
||||||
|
const float Y = 0.05F + luma / 8.24F; // 0.05..~0.90
|
||||||
|
|
||||||
|
float R = Y + 0.956F * I + 0.621F * Q;
|
||||||
|
float G = Y - 0.272F * I - 0.647F * Q;
|
||||||
|
float B = Y - 1.106F * I + 1.703F * Q;
|
||||||
|
|
||||||
|
if(R < 0) R = 0;
|
||||||
|
if(G < 0) G = 0;
|
||||||
|
if(B < 0) B = 0;
|
||||||
|
|
||||||
|
R = powf(R, 0.9F);
|
||||||
|
G = powf(G, 0.9F);
|
||||||
|
B = powf(B, 0.9F);
|
||||||
|
|
||||||
|
int r = BSPF::clamp(R * 255.F, 0.F, 255.F);
|
||||||
|
int g = BSPF::clamp(G * 255.F, 0.F, 255.F);
|
||||||
|
int b = BSPF::clamp(B * 255.F, 0.F, 255.F);
|
||||||
|
|
||||||
|
ourCustomNTSCPalette[(chroma * NUM_LUMA + luma) << 1] = (r << 16) + (g << 8) + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(timing == ConsoleTiming::pal)
|
||||||
|
{
|
||||||
|
constexpr float offset = BSPF::PI_f;
|
||||||
|
const float shift = myPhasePAL * BSPF::PI_f / 180;
|
||||||
|
constexpr float fixedShift = 22.5F * BSPF::PI_f / 180;
|
||||||
|
|
||||||
|
// colors 0, 1, 14 and 15 are grayscale
|
||||||
|
for(int chroma = 2; chroma < NUM_CHROMA - 2; chroma++)
|
||||||
|
{
|
||||||
|
int idx = NUM_CHROMA - 1 - chroma;
|
||||||
|
color[idx][0] = SATURATION * sinf(offset - fixedShift * chroma);
|
||||||
|
if ((idx & 1) == 0)
|
||||||
|
color[idx][1] = SATURATION * sinf(offset - shift * (chroma - 3.5F) / 2.F);
|
||||||
|
else
|
||||||
|
color[idx][1] = SATURATION * -sinf(offset - shift * chroma / 2.F);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int chroma = 0; chroma < NUM_CHROMA; chroma++)
|
||||||
|
{
|
||||||
|
const float U = color[chroma][0];
|
||||||
|
const float V = color[chroma][1];
|
||||||
|
|
||||||
|
for(int luma = 0; luma < NUM_LUMA; luma++)
|
||||||
|
{
|
||||||
|
const float Y = 0.05F + luma / 8.24F; // 0.05..~0.90
|
||||||
|
|
||||||
|
// Most sources
|
||||||
|
float R = Y + 1.403F * V;
|
||||||
|
float G = Y - 0.344F * U - 0.714F * V;
|
||||||
|
float B = Y + 1.770F * U;
|
||||||
|
// German Wikipedia, huh???
|
||||||
|
//float B = Y + 1 / 0.493 * U;
|
||||||
|
//float R = Y + 1 / 0.877 * V;
|
||||||
|
//float G = 1.704 * Y - 0.590 * R - 0.194 * B;
|
||||||
|
|
||||||
|
if(R < 0) R = 0.0;
|
||||||
|
if(G < 0) G = 0.0;
|
||||||
|
if(B < 0) B = 0.0;
|
||||||
|
|
||||||
|
R = powf(R, 1.2F);
|
||||||
|
G = powf(G, 1.2F);
|
||||||
|
B = powf(B, 1.2F);
|
||||||
|
|
||||||
|
int r = BSPF::clamp(R * 255.F, 0.F, 255.F);
|
||||||
|
int g = BSPF::clamp(G * 255.F, 0.F, 255.F);
|
||||||
|
int b = BSPF::clamp(B * 255.F, 0.F, 255.F);
|
||||||
|
|
||||||
|
ourCustomPALPalette[(chroma * NUM_LUMA + luma) << 1] = (r << 16) + (g << 8) + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PaletteHandler::adjustHueSaturation(int& R, int& G, int& B, float H, float S)
|
||||||
|
{
|
||||||
|
// Adapted from http://beesbuzz.biz/code/16-hsv-color-transforms
|
||||||
|
// (C) J. “Fluffy” Shagam
|
||||||
|
// License: CC BY-SA 4.0
|
||||||
|
const float su = S * cosf(-H * BSPF::PI_f);
|
||||||
|
const float sw = S * sinf(-H * BSPF::PI_f);
|
||||||
|
const float r = (.299F + .701F * su + .168F * sw) * R
|
||||||
|
+ (.587F - .587F * su + .330F * sw) * G
|
||||||
|
+ (.114F - .114F * su - .497F * sw) * B;
|
||||||
|
const float g = (.299F - .299F * su - .328F * sw) * R
|
||||||
|
+ (.587F + .413F * su + .035F * sw) * G
|
||||||
|
+ (.114F - .114F * su + .292F * sw) * B;
|
||||||
|
const float b = (.299F - .300F * su + 1.25F * sw) * R
|
||||||
|
+ (.587F - .588F * su - 1.05F * sw) * G
|
||||||
|
+ (.114F + .886F * su - .203F * sw) * B;
|
||||||
|
|
||||||
|
R = BSPF::clamp(r, 0.F, 255.F);
|
||||||
|
G = BSPF::clamp(g, 0.F, 255.F);
|
||||||
|
B = BSPF::clamp(b, 0.F, 255.F);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const PaletteArray PaletteHandler::ourNTSCPalette = {
|
||||||
|
0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0,
|
||||||
|
0xaaaaaa, 0, 0xc0c0c0, 0, 0xd6d6d6, 0, 0xececec, 0,
|
||||||
|
0x484800, 0, 0x69690f, 0, 0x86861d, 0, 0xa2a22a, 0,
|
||||||
|
0xbbbb35, 0, 0xd2d240, 0, 0xe8e84a, 0, 0xfcfc54, 0,
|
||||||
|
0x7c2c00, 0, 0x904811, 0, 0xa26221, 0, 0xb47a30, 0,
|
||||||
|
0xc3903d, 0, 0xd2a44a, 0, 0xdfb755, 0, 0xecc860, 0,
|
||||||
|
0x901c00, 0, 0xa33915, 0, 0xb55328, 0, 0xc66c3a, 0,
|
||||||
|
0xd5824a, 0, 0xe39759, 0, 0xf0aa67, 0, 0xfcbc74, 0,
|
||||||
|
0x940000, 0, 0xa71a1a, 0, 0xb83232, 0, 0xc84848, 0,
|
||||||
|
0xd65c5c, 0, 0xe46f6f, 0, 0xf08080, 0, 0xfc9090, 0,
|
||||||
|
0x840064, 0, 0x97197a, 0, 0xa8308f, 0, 0xb846a2, 0,
|
||||||
|
0xc659b3, 0, 0xd46cc3, 0, 0xe07cd2, 0, 0xec8ce0, 0,
|
||||||
|
0x500084, 0, 0x68199a, 0, 0x7d30ad, 0, 0x9246c0, 0,
|
||||||
|
0xa459d0, 0, 0xb56ce0, 0, 0xc57cee, 0, 0xd48cfc, 0,
|
||||||
|
0x140090, 0, 0x331aa3, 0, 0x4e32b5, 0, 0x6848c6, 0,
|
||||||
|
0x7f5cd5, 0, 0x956fe3, 0, 0xa980f0, 0, 0xbc90fc, 0,
|
||||||
|
0x000094, 0, 0x181aa7, 0, 0x2d32b8, 0, 0x4248c8, 0,
|
||||||
|
0x545cd6, 0, 0x656fe4, 0, 0x7580f0, 0, 0x8490fc, 0,
|
||||||
|
0x001c88, 0, 0x183b9d, 0, 0x2d57b0, 0, 0x4272c2, 0,
|
||||||
|
0x548ad2, 0, 0x65a0e1, 0, 0x75b5ef, 0, 0x84c8fc, 0,
|
||||||
|
0x003064, 0, 0x185080, 0, 0x2d6d98, 0, 0x4288b0, 0,
|
||||||
|
0x54a0c5, 0, 0x65b7d9, 0, 0x75cceb, 0, 0x84e0fc, 0,
|
||||||
|
0x004030, 0, 0x18624e, 0, 0x2d8169, 0, 0x429e82, 0,
|
||||||
|
0x54b899, 0, 0x65d1ae, 0, 0x75e7c2, 0, 0x84fcd4, 0,
|
||||||
|
0x004400, 0, 0x1a661a, 0, 0x328432, 0, 0x48a048, 0,
|
||||||
|
0x5cba5c, 0, 0x6fd26f, 0, 0x80e880, 0, 0x90fc90, 0,
|
||||||
|
0x143c00, 0, 0x355f18, 0, 0x527e2d, 0, 0x6e9c42, 0,
|
||||||
|
0x87b754, 0, 0x9ed065, 0, 0xb4e775, 0, 0xc8fc84, 0,
|
||||||
|
0x303800, 0, 0x505916, 0, 0x6d762b, 0, 0x88923e, 0,
|
||||||
|
0xa0ab4f, 0, 0xb7c25f, 0, 0xccd86e, 0, 0xe0ec7c, 0,
|
||||||
|
0x482c00, 0, 0x694d14, 0, 0x866a26, 0, 0xa28638, 0,
|
||||||
|
0xbb9f47, 0, 0xd2b656, 0, 0xe8cc63, 0, 0xfce070, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const PaletteArray PaletteHandler::ourPALPalette = {
|
||||||
|
0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 180 0
|
||||||
|
0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0, // was 0x111111..0xcccccc
|
||||||
|
0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 198 1
|
||||||
|
0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
|
||||||
|
0x1d0f00, 0, 0x3f2700, 0, 0x614900, 0, 0x836b01, 0, // 1b0 2
|
||||||
|
0xa58d23, 0, 0xc7af45, 0, 0xe9d167, 0, 0xffe789, 0, // was ..0xfff389
|
||||||
|
0x002400, 0, 0x004600, 0, 0x216800, 0, 0x438a07, 0, // 1c8 3
|
||||||
|
0x65ac29, 0, 0x87ce4b, 0, 0xa9f06d, 0, 0xcbff8f, 0,
|
||||||
|
0x340000, 0, 0x561400, 0, 0x783602, 0, 0x9a5824, 0, // 1e0 4
|
||||||
|
0xbc7a46, 0, 0xde9c68, 0, 0xffbe8a, 0, 0xffd0ad, 0, // was ..0xffe0ac
|
||||||
|
0x002700, 0, 0x004900, 0, 0x0c6b0c, 0, 0x2e8d2e, 0, // 1f8 5
|
||||||
|
0x50af50, 0, 0x72d172, 0, 0x94f394, 0, 0xb6ffb6, 0,
|
||||||
|
0x3d0008, 0, 0x610511, 0, 0x832733, 0, 0xa54955, 0, // 210 6
|
||||||
|
0xc76b77, 0, 0xe98d99, 0, 0xffafbb, 0, 0xffd1d7, 0, // was 0x3f0000..0xffd1dd
|
||||||
|
0x001e12, 0, 0x004228, 0, 0x046540, 0, 0x268762, 0, // 228 7
|
||||||
|
0x48a984, 0, 0x6acba6, 0, 0x8cedc8, 0, 0xafffe0, 0, // was 0x002100, 0x00431e..0xaeffff
|
||||||
|
0x300025, 0, 0x5f0047, 0, 0x811e69, 0, 0xa3408b, 0, // 240 8
|
||||||
|
0xc562ad, 0, 0xe784cf, 0, 0xffa8ea, 0, 0xffc9f2, 0, // was ..0xffa6f1, 0xffc8ff
|
||||||
|
0x001431, 0, 0x003653, 0, 0x0a5875, 0, 0x2c7a97, 0, // 258 9
|
||||||
|
0x4e9cb9, 0, 0x70bedb, 0, 0x92e0fd, 0, 0xb4ffff, 0,
|
||||||
|
0x2c0052, 0, 0x4e0074, 0, 0x701d96, 0, 0x923fb8, 0, // 270 a
|
||||||
|
0xb461da, 0, 0xd683fc, 0, 0xe2a5ff, 0, 0xeec9ff, 0, // was ..0xf8a5ff, 0xffc7ff
|
||||||
|
0x001759, 0, 0x00247c, 0, 0x1d469e, 0, 0x3f68c0, 0, // 288 b
|
||||||
|
0x618ae2, 0, 0x83acff, 0, 0xa5ceff, 0, 0xc7f0ff, 0,
|
||||||
|
0x12006d, 0, 0x34038f, 0, 0x5625b1, 0, 0x7847d3, 0, // 2a0 c
|
||||||
|
0x9a69f5, 0, 0xb48cff, 0, 0xc9adff, 0, 0xe1d1ff, 0, // was ..0xbc8bff, 0xdeadff, 0xffcfff,
|
||||||
|
0x000070, 0, 0x161292, 0, 0x3834b4, 0, 0x5a56d6, 0, // 2b8 d
|
||||||
|
0x7c78f8, 0, 0x9e9aff, 0, 0xc0bcff, 0, 0xe2deff, 0,
|
||||||
|
0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 2d0 e
|
||||||
|
0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
|
||||||
|
0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 2e8 f
|
||||||
|
0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const PaletteArray PaletteHandler::ourSECAMPalette = {
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const PaletteArray PaletteHandler::ourNTSCPaletteZ26 = {
|
||||||
|
0x000000, 0, 0x505050, 0, 0x646464, 0, 0x787878, 0,
|
||||||
|
0x8c8c8c, 0, 0xa0a0a0, 0, 0xb4b4b4, 0, 0xc8c8c8, 0,
|
||||||
|
0x445400, 0, 0x586800, 0, 0x6c7c00, 0, 0x809000, 0,
|
||||||
|
0x94a414, 0, 0xa8b828, 0, 0xbccc3c, 0, 0xd0e050, 0,
|
||||||
|
0x673900, 0, 0x7b4d00, 0, 0x8f6100, 0, 0xa37513, 0,
|
||||||
|
0xb78927, 0, 0xcb9d3b, 0, 0xdfb14f, 0, 0xf3c563, 0,
|
||||||
|
0x7b2504, 0, 0x8f3918, 0, 0xa34d2c, 0, 0xb76140, 0,
|
||||||
|
0xcb7554, 0, 0xdf8968, 0, 0xf39d7c, 0, 0xffb190, 0,
|
||||||
|
0x7d122c, 0, 0x912640, 0, 0xa53a54, 0, 0xb94e68, 0,
|
||||||
|
0xcd627c, 0, 0xe17690, 0, 0xf58aa4, 0, 0xff9eb8, 0,
|
||||||
|
0x730871, 0, 0x871c85, 0, 0x9b3099, 0, 0xaf44ad, 0,
|
||||||
|
0xc358c1, 0, 0xd76cd5, 0, 0xeb80e9, 0, 0xff94fd, 0,
|
||||||
|
0x5d0b92, 0, 0x711fa6, 0, 0x8533ba, 0, 0x9947ce, 0,
|
||||||
|
0xad5be2, 0, 0xc16ff6, 0, 0xd583ff, 0, 0xe997ff, 0,
|
||||||
|
0x401599, 0, 0x5429ad, 0, 0x683dc1, 0, 0x7c51d5, 0,
|
||||||
|
0x9065e9, 0, 0xa479fd, 0, 0xb88dff, 0, 0xcca1ff, 0,
|
||||||
|
0x252593, 0, 0x3939a7, 0, 0x4d4dbb, 0, 0x6161cf, 0,
|
||||||
|
0x7575e3, 0, 0x8989f7, 0, 0x9d9dff, 0, 0xb1b1ff, 0,
|
||||||
|
0x0f3480, 0, 0x234894, 0, 0x375ca8, 0, 0x4b70bc, 0,
|
||||||
|
0x5f84d0, 0, 0x7398e4, 0, 0x87acf8, 0, 0x9bc0ff, 0,
|
||||||
|
0x04425a, 0, 0x18566e, 0, 0x2c6a82, 0, 0x407e96, 0,
|
||||||
|
0x5492aa, 0, 0x68a6be, 0, 0x7cbad2, 0, 0x90cee6, 0,
|
||||||
|
0x044f30, 0, 0x186344, 0, 0x2c7758, 0, 0x408b6c, 0,
|
||||||
|
0x549f80, 0, 0x68b394, 0, 0x7cc7a8, 0, 0x90dbbc, 0,
|
||||||
|
0x0f550a, 0, 0x23691e, 0, 0x377d32, 0, 0x4b9146, 0,
|
||||||
|
0x5fa55a, 0, 0x73b96e, 0, 0x87cd82, 0, 0x9be196, 0,
|
||||||
|
0x1f5100, 0, 0x336505, 0, 0x477919, 0, 0x5b8d2d, 0,
|
||||||
|
0x6fa141, 0, 0x83b555, 0, 0x97c969, 0, 0xabdd7d, 0,
|
||||||
|
0x344600, 0, 0x485a00, 0, 0x5c6e14, 0, 0x708228, 0,
|
||||||
|
0x84963c, 0, 0x98aa50, 0, 0xacbe64, 0, 0xc0d278, 0,
|
||||||
|
0x463e00, 0, 0x5a5205, 0, 0x6e6619, 0, 0x827a2d, 0,
|
||||||
|
0x968e41, 0, 0xaaa255, 0, 0xbeb669, 0, 0xd2ca7d, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const PaletteArray PaletteHandler::ourPALPaletteZ26 = {
|
||||||
|
0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
|
||||||
|
0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
|
||||||
|
0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
|
||||||
|
0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
|
||||||
|
0x533a00, 0, 0x674e00, 0, 0x7b6203, 0, 0x8f7617, 0,
|
||||||
|
0xa38a2b, 0, 0xb79e3f, 0, 0xcbb253, 0, 0xdfc667, 0,
|
||||||
|
0x1b5800, 0, 0x2f6c00, 0, 0x438001, 0, 0x579415, 0,
|
||||||
|
0x6ba829, 0, 0x7fbc3d, 0, 0x93d051, 0, 0xa7e465, 0,
|
||||||
|
0x6a2900, 0, 0x7e3d12, 0, 0x925126, 0, 0xa6653a, 0,
|
||||||
|
0xba794e, 0, 0xce8d62, 0, 0xe2a176, 0, 0xf6b58a, 0,
|
||||||
|
0x075b00, 0, 0x1b6f11, 0, 0x2f8325, 0, 0x439739, 0,
|
||||||
|
0x57ab4d, 0, 0x6bbf61, 0, 0x7fd375, 0, 0x93e789, 0,
|
||||||
|
0x741b2f, 0, 0x882f43, 0, 0x9c4357, 0, 0xb0576b, 0,
|
||||||
|
0xc46b7f, 0, 0xd87f93, 0, 0xec93a7, 0, 0xffa7bb, 0,
|
||||||
|
0x00572e, 0, 0x106b42, 0, 0x247f56, 0, 0x38936a, 0,
|
||||||
|
0x4ca77e, 0, 0x60bb92, 0, 0x74cfa6, 0, 0x88e3ba, 0,
|
||||||
|
0x6d165f, 0, 0x812a73, 0, 0x953e87, 0, 0xa9529b, 0,
|
||||||
|
0xbd66af, 0, 0xd17ac3, 0, 0xe58ed7, 0, 0xf9a2eb, 0,
|
||||||
|
0x014c5e, 0, 0x156072, 0, 0x297486, 0, 0x3d889a, 0,
|
||||||
|
0x519cae, 0, 0x65b0c2, 0, 0x79c4d6, 0, 0x8dd8ea, 0,
|
||||||
|
0x5f1588, 0, 0x73299c, 0, 0x873db0, 0, 0x9b51c4, 0,
|
||||||
|
0xaf65d8, 0, 0xc379ec, 0, 0xd78dff, 0, 0xeba1ff, 0,
|
||||||
|
0x123b87, 0, 0x264f9b, 0, 0x3a63af, 0, 0x4e77c3, 0,
|
||||||
|
0x628bd7, 0, 0x769feb, 0, 0x8ab3ff, 0, 0x9ec7ff, 0,
|
||||||
|
0x451e9d, 0, 0x5932b1, 0, 0x6d46c5, 0, 0x815ad9, 0,
|
||||||
|
0x956eed, 0, 0xa982ff, 0, 0xbd96ff, 0, 0xd1aaff, 0,
|
||||||
|
0x2a2b9e, 0, 0x3e3fb2, 0, 0x5253c6, 0, 0x6667da, 0,
|
||||||
|
0x7a7bee, 0, 0x8e8fff, 0, 0xa2a3ff, 0, 0xb6b7ff, 0,
|
||||||
|
0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
|
||||||
|
0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
|
||||||
|
0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
|
||||||
|
0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const PaletteArray PaletteHandler::ourSECAMPaletteZ26 = {
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
|
||||||
|
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
|
||||||
|
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteArray PaletteHandler::ourUserNTSCPalette = { 0 }; // filled from external file
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteArray PaletteHandler::ourUserPALPalette = { 0 }; // filled from external file
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteArray PaletteHandler::ourUserSECAMPalette = { 0 }; // filled from external file
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteArray PaletteHandler::ourCustomNTSCPalette = { 0 }; // filled by function
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PaletteArray PaletteHandler::ourCustomPALPalette = { 0 }; // filled by function
|
|
@ -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-2020 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.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#ifndef PALETTE_HANDLER_HXX
|
||||||
|
#define PALETTE_HANDLER_HXX
|
||||||
|
|
||||||
|
#include "bspf.hxx"
|
||||||
|
#include "OSystem.hxx"
|
||||||
|
#include "ConsoleTiming.hxx"
|
||||||
|
#include "EventHandlerConstants.hxx"
|
||||||
|
|
||||||
|
class PaletteHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Setting names of palette types
|
||||||
|
static constexpr const char* SETTING_STANDARD = "standard";
|
||||||
|
static constexpr const char* SETTING_Z26 = "z26";
|
||||||
|
static constexpr const char* SETTING_USER = "user";
|
||||||
|
static constexpr const char* SETTING_CUSTOM = "custom";
|
||||||
|
|
||||||
|
// Phase shift default and limits
|
||||||
|
static constexpr float DEF_NTSC_SHIFT = 26.2F;
|
||||||
|
static constexpr float DEF_PAL_SHIFT = 31.3F; // ~= 360 / 11.5
|
||||||
|
static constexpr float MAX_SHIFT = 4.5F;
|
||||||
|
|
||||||
|
enum Adjustables {
|
||||||
|
PHASE_SHIFT,
|
||||||
|
HUE,
|
||||||
|
SATURATION,
|
||||||
|
CONTRAST,
|
||||||
|
BRIGHTNESS,
|
||||||
|
GAMMA
|
||||||
|
};
|
||||||
|
|
||||||
|
// Externally used adjustment parameters
|
||||||
|
struct Adjustable {
|
||||||
|
float phaseNtsc{0.F}, phasePal{0.F};
|
||||||
|
uInt32 hue{0}, saturation{0}, contrast{0}, brightness{0}, gamma{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
PaletteHandler(OSystem& system);
|
||||||
|
virtual ~PaletteHandler() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Cycle through available palettes.
|
||||||
|
|
||||||
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
|
*/
|
||||||
|
void cyclePalette(int direction = +1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cycle through each palette adjustable.
|
||||||
|
|
||||||
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
|
*/
|
||||||
|
void cycleAdjustable(int direction = +1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Increase or decrease given palette adjustable.
|
||||||
|
|
||||||
|
@param adjustable The adjustable to change
|
||||||
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
|
*/
|
||||||
|
void changeAdjustable(int adjustable, int direction);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Increase or decrease current palette adjustable.
|
||||||
|
|
||||||
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
|
*/
|
||||||
|
void changeCurrentAdjustable(int direction = +1);
|
||||||
|
|
||||||
|
// Load adjustables from settings
|
||||||
|
void loadConfig(const Settings& settings);
|
||||||
|
|
||||||
|
// Save adjustables to settings
|
||||||
|
void saveConfig(Settings& settings) const;
|
||||||
|
|
||||||
|
// Set adjustables
|
||||||
|
void setAdjustables(const Adjustable& adjustable);
|
||||||
|
|
||||||
|
// Retrieve current adjustables
|
||||||
|
void getAdjustables(Adjustable& adjustable) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the palette according to the given palette name.
|
||||||
|
|
||||||
|
@param name The palette to switch to
|
||||||
|
*/
|
||||||
|
void setPalette(const string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the palette from current settings.
|
||||||
|
*/
|
||||||
|
void setPalette();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr char DEGREE = 0x1c;
|
||||||
|
|
||||||
|
enum PaletteType {
|
||||||
|
Standard,
|
||||||
|
Z26,
|
||||||
|
User,
|
||||||
|
Custom,
|
||||||
|
NumTypes,
|
||||||
|
MinType = Standard,
|
||||||
|
MaxType = Custom
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert adjustables from/to 100% scale
|
||||||
|
*/
|
||||||
|
static constexpr float scaleFrom100(float x) { return (x / 50.F) - 1.F; }
|
||||||
|
static constexpr uInt32 scaleTo100(float x) { return uInt32(50.0001F * (x + 1.F)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert palette settings name to enumeration.
|
||||||
|
|
||||||
|
@param name The given palette's settings name
|
||||||
|
|
||||||
|
@return The palette type
|
||||||
|
*/
|
||||||
|
PaletteType toPaletteType(const string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert enumeration to palette settings name.
|
||||||
|
|
||||||
|
@param type The given palette type
|
||||||
|
|
||||||
|
@return The palette's settings name
|
||||||
|
*/
|
||||||
|
string toPaletteName(PaletteType type) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Display current adjustable with gauge bar message
|
||||||
|
*/
|
||||||
|
void showAdjustableMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Change the "phase shift" variable.
|
||||||
|
Note that there are two of these (NTSC and PAL). The currently
|
||||||
|
active mode will determine which one is used.
|
||||||
|
|
||||||
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
|
*/
|
||||||
|
void changeColorPhaseShift(int direction = +1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generates a custom palette, based on user defined phase shifts.
|
||||||
|
|
||||||
|
@param timing Use NTSC or PAL phase shift and generate according palette
|
||||||
|
*/
|
||||||
|
void generateCustomPalette(ConsoleTiming timing);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create new palette by applying palette adjustments on given palette.
|
||||||
|
|
||||||
|
@param source The palette which should be adjusted
|
||||||
|
|
||||||
|
@return An adjusted palette
|
||||||
|
*/
|
||||||
|
PaletteArray adjustedPalette(const PaletteArray& source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adjust hue and saturation for given RGB values.
|
||||||
|
|
||||||
|
@param R The red value to adjust
|
||||||
|
@param G The green value to adjust
|
||||||
|
@param B The blue value to adjust
|
||||||
|
@param H The hue adjustment value
|
||||||
|
@param S The saturation
|
||||||
|
*/
|
||||||
|
void adjustHueSaturation(int& R, int& G, int& B, float H, float S);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Loads a user-defined palette file (from OSystem::paletteFile), filling the
|
||||||
|
appropriate user-defined palette arrays.
|
||||||
|
*/
|
||||||
|
void loadUserPalette();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int NUM_ADJUSTABLES = 6;
|
||||||
|
|
||||||
|
OSystem& myOSystem;
|
||||||
|
|
||||||
|
// The currently selected adjustable
|
||||||
|
uInt32 myCurrentAdjustable{0};
|
||||||
|
|
||||||
|
struct AdjustableTag {
|
||||||
|
const char* const name{nullptr};
|
||||||
|
float* value{nullptr};
|
||||||
|
};
|
||||||
|
const std::array<AdjustableTag, NUM_ADJUSTABLES> myAdjustables =
|
||||||
|
{ {
|
||||||
|
{ "phase shift", nullptr },
|
||||||
|
{ "hue", &myHue },
|
||||||
|
{ "saturation", &mySaturation },
|
||||||
|
{ "contrast", &myContrast },
|
||||||
|
{ "brightness", &myBrightness },
|
||||||
|
{ "gamma", &myGamma },
|
||||||
|
} };
|
||||||
|
|
||||||
|
// NTSC and PAL color phase shifts
|
||||||
|
float myPhaseNTSC{DEF_NTSC_SHIFT};
|
||||||
|
float myPhasePAL{DEF_PAL_SHIFT};
|
||||||
|
// range -1.0 to +1.0 (as in AtariNTSC)
|
||||||
|
// Basic parameters
|
||||||
|
float myHue{0.0F}; // -1 = -180 degrees +1 = +180 degrees
|
||||||
|
float mySaturation{0.0F}; // -1 = grayscale (0.0) +1 = oversaturated colors (2.0)
|
||||||
|
float myContrast{0.0F}; // -1 = dark (0.5) +1 = light (1.5)
|
||||||
|
float myBrightness{0.0F}; // -1 = dark (0.5) +1 = light (1.5)
|
||||||
|
// Advanced parameters
|
||||||
|
float myGamma{0.0F}; // -1 = dark (1.5) +1 = light (0.5)
|
||||||
|
|
||||||
|
// Indicates whether an external palette was found and
|
||||||
|
// successfully loaded
|
||||||
|
bool myUserPaletteDefined{false};
|
||||||
|
|
||||||
|
// Table of RGB values for NTSC, PAL and SECAM
|
||||||
|
static const PaletteArray ourNTSCPalette;
|
||||||
|
static const PaletteArray ourPALPalette;
|
||||||
|
static const PaletteArray ourSECAMPalette;
|
||||||
|
|
||||||
|
// Table of RGB values for NTSC, PAL and SECAM - Z26 version
|
||||||
|
static const PaletteArray ourNTSCPaletteZ26;
|
||||||
|
static const PaletteArray ourPALPaletteZ26;
|
||||||
|
static const PaletteArray ourSECAMPaletteZ26;
|
||||||
|
|
||||||
|
// Table of RGB values for NTSC, PAL and SECAM - user-defined
|
||||||
|
static PaletteArray ourUserNTSCPalette;
|
||||||
|
static PaletteArray ourUserPALPalette;
|
||||||
|
static PaletteArray ourUserSECAMPalette;
|
||||||
|
|
||||||
|
// Table of RGB values for NTSC, PAL - custom-defined and generated
|
||||||
|
static PaletteArray ourCustomNTSCPalette;
|
||||||
|
static PaletteArray ourCustomPALPalette;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PaletteHandler() = delete;
|
||||||
|
PaletteHandler(const PaletteHandler&) = delete;
|
||||||
|
PaletteHandler(PaletteHandler&&) = delete;
|
||||||
|
PaletteHandler& operator=(const PaletteHandler&) = delete;
|
||||||
|
PaletteHandler& operator=(const PaletteHandler&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PALETTE_HANDLER_HXX
|
|
@ -111,7 +111,8 @@ struct Rect
|
||||||
Rect() {}
|
Rect() {}
|
||||||
explicit Rect(const Size& s) : bottom(s.h), right(s.w) { assert(valid()); }
|
explicit Rect(const Size& s) : bottom(s.h), right(s.w) { assert(valid()); }
|
||||||
Rect(uInt32 w, uInt32 h) : bottom(h), right(w) { assert(valid()); }
|
Rect(uInt32 w, uInt32 h) : bottom(h), right(w) { assert(valid()); }
|
||||||
Rect(const Point& p, uInt32 w, uInt32 h) : top(p.y), left(p.x), bottom(h), right(w) { assert(valid()); }
|
Rect(const Point& p, uInt32 w, uInt32 h)
|
||||||
|
: top(p.y), left(p.x), bottom(p.y + h), right(p.x + w) { assert(valid()); }
|
||||||
Rect(uInt32 x1, uInt32 y1, uInt32 x2, uInt32 y2) : top(y1), left(x1), bottom(y2), right(x2) { assert(valid()); }
|
Rect(uInt32 x1, uInt32 y1, uInt32 x2, uInt32 y2) : top(y1), left(x1), bottom(y2), right(x2) { assert(valid()); }
|
||||||
|
|
||||||
uInt32 x() const { return left; }
|
uInt32 x() const { return left; }
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
|
#include "Console.hxx"
|
||||||
#include "Serializer.hxx"
|
#include "Serializer.hxx"
|
||||||
#include "StateManager.hxx"
|
#include "StateManager.hxx"
|
||||||
#include "TIA.hxx"
|
#include "TIA.hxx"
|
||||||
|
@ -180,7 +181,8 @@ uInt32 RewindManager::rewindStates(uInt32 numStates)
|
||||||
else
|
else
|
||||||
message = "Rewind not possible";
|
message = "Rewind not possible";
|
||||||
|
|
||||||
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE)
|
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
|
||||||
|
&& myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK)
|
||||||
myOSystem.frameBuffer().showMessage(message);
|
myOSystem.frameBuffer().showMessage(message);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +216,8 @@ uInt32 RewindManager::unwindStates(uInt32 numStates)
|
||||||
else
|
else
|
||||||
message = "Unwind not possible";
|
message = "Unwind not possible";
|
||||||
|
|
||||||
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE)
|
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
|
||||||
|
&& myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK)
|
||||||
myOSystem.frameBuffer().showMessage(message);
|
myOSystem.frameBuffer().showMessage(message);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,13 @@
|
||||||
#ifndef SOUND_NULL_HXX
|
#ifndef SOUND_NULL_HXX
|
||||||
#define SOUND_NULL_HXX
|
#define SOUND_NULL_HXX
|
||||||
|
|
||||||
|
class OSystem;
|
||||||
|
class AudioQueue;
|
||||||
|
class EmulationTiming;
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Logger.hxx"
|
#include "Logger.hxx"
|
||||||
#include "Sound.hxx"
|
#include "Sound.hxx"
|
||||||
#include "OSystem.hxx"
|
|
||||||
#include "AudioQueue.hxx"
|
|
||||||
#include "EmulationTiming.hxx"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This class implements a Null sound object, where-by sound generation
|
This class implements a Null sound object, where-by sound generation
|
||||||
|
@ -96,10 +97,16 @@ class SoundNull : public Sound
|
||||||
/**
|
/**
|
||||||
Adjusts the volume of the sound device based on the given direction.
|
Adjusts the volume of the sound device based on the given direction.
|
||||||
|
|
||||||
@param direction Increase or decrease the current volume by a predefined
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
amount based on the direction (1 = increase, -1 =decrease)
|
|
||||||
*/
|
*/
|
||||||
void adjustVolume(Int8 direction) override { }
|
void adjustVolume(int direction = 1) override { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the audio device.
|
||||||
|
|
||||||
|
@param device The number of the device to select (0 = default).
|
||||||
|
*/
|
||||||
|
void setDevice(uInt32 device) override { };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This method is called to provide information about the sound device.
|
This method is called to provide information about the sound device.
|
||||||
|
|
|
@ -56,6 +56,8 @@ SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queryHardware(myDevices);
|
||||||
|
|
||||||
SDL_zero(myHardwareSpec);
|
SDL_zero(myHardwareSpec);
|
||||||
if(!openDevice())
|
if(!openDevice())
|
||||||
return;
|
return;
|
||||||
|
@ -76,6 +78,29 @@ SoundSDL2::~SoundSDL2()
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL2::queryHardware(VariantList& devices)
|
||||||
|
{
|
||||||
|
ASSERT_MAIN_THREAD;
|
||||||
|
|
||||||
|
int numDevices = SDL_GetNumAudioDevices(0);
|
||||||
|
|
||||||
|
// log the available audio devices
|
||||||
|
ostringstream s;
|
||||||
|
s << "Supported audio devices (" << numDevices << "):";
|
||||||
|
Logger::debug(s.str());
|
||||||
|
|
||||||
|
VarList::push_back(devices, "Default", 0);
|
||||||
|
for(int i = 0; i < numDevices; ++i) {
|
||||||
|
ostringstream ss;
|
||||||
|
|
||||||
|
ss << " " << i + 1 << ": " << SDL_GetAudioDeviceName(i, 0);
|
||||||
|
Logger::debug(ss.str());
|
||||||
|
|
||||||
|
VarList::push_back(devices, SDL_GetAudioDeviceName(i, 0), i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool SoundSDL2::openDevice()
|
bool SoundSDL2::openDevice()
|
||||||
{
|
{
|
||||||
|
@ -91,7 +116,11 @@ bool SoundSDL2::openDevice()
|
||||||
|
|
||||||
if(myIsInitializedFlag)
|
if(myIsInitializedFlag)
|
||||||
SDL_CloseAudioDevice(myDevice);
|
SDL_CloseAudioDevice(myDevice);
|
||||||
myDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &myHardwareSpec,
|
|
||||||
|
myDeviceId = BSPF::clamp(myAudioSettings.device(), 0u, uInt32(myDevices.size() - 1));
|
||||||
|
const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
|
||||||
|
|
||||||
|
myDevice = SDL_OpenAudioDevice(device, 0, &desired, &myHardwareSpec,
|
||||||
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||||
|
|
||||||
if(myDevice == 0)
|
if(myDevice == 0)
|
||||||
|
@ -126,7 +155,8 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
|
||||||
// Do we need to re-open the sound device?
|
// Do we need to re-open the sound device?
|
||||||
// Only do this when absolutely necessary
|
// Only do this when absolutely necessary
|
||||||
if(myAudioSettings.sampleRate() != uInt32(myHardwareSpec.freq) ||
|
if(myAudioSettings.sampleRate() != uInt32(myHardwareSpec.freq) ||
|
||||||
myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples))
|
myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples) ||
|
||||||
|
myAudioSettings.device() != myDeviceId)
|
||||||
openDevice();
|
openDevice();
|
||||||
|
|
||||||
myEmulationTiming = emulationTiming;
|
myEmulationTiming = emulationTiming;
|
||||||
|
@ -186,16 +216,31 @@ bool SoundSDL2::mute(bool state)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool SoundSDL2::toggleMute()
|
bool SoundSDL2::toggleMute()
|
||||||
{
|
{
|
||||||
bool enabled = myAudioSettings.enabled();
|
bool enabled = !myAudioSettings.enabled();
|
||||||
|
|
||||||
setEnabled(!enabled);
|
setEnabled(enabled);
|
||||||
myOSystem.console().initializeAudio();
|
myOSystem.console().initializeAudio();
|
||||||
|
|
||||||
string message = "Sound ";
|
string message = "Sound ";
|
||||||
message += !enabled ? "unmuted" : "muted";
|
message += enabled ? "unmuted" : "muted";
|
||||||
|
|
||||||
myOSystem.frameBuffer().showMessage(message);
|
myOSystem.frameBuffer().showMessage(message);
|
||||||
|
|
||||||
|
//ostringstream strval;
|
||||||
|
//uInt32 volume;
|
||||||
|
//// Now show an onscreen message
|
||||||
|
//if(enabled)
|
||||||
|
//{
|
||||||
|
// volume = myVolume;
|
||||||
|
// strval << volume << "%";
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// volume = 0;
|
||||||
|
// strval << "Muted";
|
||||||
|
//}
|
||||||
|
//myOSystem.frameBuffer().showMessage("Volume", strval.str(), volume);
|
||||||
|
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,20 +259,12 @@ void SoundSDL2::setVolume(uInt32 percent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::adjustVolume(Int8 direction)
|
void SoundSDL2::adjustVolume(int direction)
|
||||||
{
|
{
|
||||||
ostringstream strval;
|
ostringstream strval;
|
||||||
string message;
|
|
||||||
|
|
||||||
Int32 percent = myVolume;
|
Int32 percent = myVolume;
|
||||||
|
|
||||||
if(direction == -1)
|
percent = BSPF::clamp(percent + direction * 2, 0, 100);
|
||||||
percent -= 2;
|
|
||||||
else if(direction == 1)
|
|
||||||
percent += 2;
|
|
||||||
|
|
||||||
if((percent < 0) || (percent > 100))
|
|
||||||
return;
|
|
||||||
|
|
||||||
setVolume(percent);
|
setVolume(percent);
|
||||||
|
|
||||||
|
@ -241,11 +278,11 @@ void SoundSDL2::adjustVolume(Int8 direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now show an onscreen message
|
// Now show an onscreen message
|
||||||
strval << percent;
|
if(percent)
|
||||||
message = "Volume set to ";
|
strval << percent << "%";
|
||||||
message += strval.str();
|
else
|
||||||
|
strval << "Off";
|
||||||
myOSystem.frameBuffer().showMessage(message);
|
myOSystem.frameBuffer().showMessage("Volume", strval.str(), percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -254,6 +291,7 @@ string SoundSDL2::about() const
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
buf << "Sound enabled:" << endl
|
buf << "Sound enabled:" << endl
|
||||||
<< " Volume: " << myVolume << "%" << endl
|
<< " Volume: " << myVolume << "%" << endl
|
||||||
|
<< " Device: " << myDevices.at(myDeviceId).first << endl
|
||||||
<< " Channels: " << uInt32(myHardwareSpec.channels)
|
<< " Channels: " << uInt32(myHardwareSpec.channels)
|
||||||
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << endl
|
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << endl
|
||||||
<< " Preset: ";
|
<< " Preset: ";
|
||||||
|
|
|
@ -24,12 +24,12 @@ class OSystem;
|
||||||
class AudioQueue;
|
class AudioQueue;
|
||||||
class EmulationTiming;
|
class EmulationTiming;
|
||||||
class AudioSettings;
|
class AudioSettings;
|
||||||
|
class Resampler;
|
||||||
|
|
||||||
#include "SDL_lib.hxx"
|
#include "SDL_lib.hxx"
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Sound.hxx"
|
#include "Sound.hxx"
|
||||||
#include "audio/Resampler.hxx"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This class implements the sound API for SDL.
|
This class implements the sound API for SDL.
|
||||||
|
@ -98,10 +98,9 @@ class SoundSDL2 : public Sound
|
||||||
/**
|
/**
|
||||||
Adjusts the volume of the sound device based on the given direction.
|
Adjusts the volume of the sound device based on the given direction.
|
||||||
|
|
||||||
@param direction Increase or decrease the current volume by a predefined
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
amount based on the direction (1 = increase, -1 = decrease)
|
|
||||||
*/
|
*/
|
||||||
void adjustVolume(Int8 direction) override;
|
void adjustVolume(int direction = 1) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This method is called to provide information about the sound device.
|
This method is called to provide information about the sound device.
|
||||||
|
@ -109,6 +108,13 @@ class SoundSDL2 : public Sound
|
||||||
string about() const override;
|
string about() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/**
|
||||||
|
This method is called to query the audio devices.
|
||||||
|
|
||||||
|
@param devices List of device names
|
||||||
|
*/
|
||||||
|
void queryHardware(VariantList& devices) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Invoked by the sound callback to process the next sound fragment.
|
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 stream is 16-bits (even though the callback is 8-bits), since
|
||||||
|
@ -140,6 +146,8 @@ class SoundSDL2 : public Sound
|
||||||
// Audio specification structure
|
// Audio specification structure
|
||||||
SDL_AudioSpec myHardwareSpec;
|
SDL_AudioSpec myHardwareSpec;
|
||||||
|
|
||||||
|
uInt32 myDeviceId{0};
|
||||||
|
|
||||||
SDL_AudioDeviceID myDevice{0};
|
SDL_AudioDeviceID myDevice{0};
|
||||||
|
|
||||||
shared_ptr<AudioQueue> myAudioQueue;
|
shared_ptr<AudioQueue> myAudioQueue;
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
*/
|
*/
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <class T, uInt32 CAPACITY = 50>
|
template <typename T, uInt32 CAPACITY = 50>
|
||||||
class FixedStack
|
class FixedStack
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -299,15 +299,14 @@ void StateManager::saveState(int slot)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void StateManager::changeState(int direction)
|
void StateManager::changeState(int direction)
|
||||||
{
|
{
|
||||||
myCurrentSlot += direction;
|
myCurrentSlot = BSPF::clampw(myCurrentSlot + direction, 0, 9);
|
||||||
if (myCurrentSlot < 0)
|
|
||||||
myCurrentSlot = 9;
|
|
||||||
else
|
|
||||||
myCurrentSlot %= 10;
|
|
||||||
|
|
||||||
// Print appropriate message
|
// Print appropriate message
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
buf << "Changed to slot " << myCurrentSlot;
|
if(direction)
|
||||||
|
buf << "Changed to state slot " << myCurrentSlot;
|
||||||
|
else
|
||||||
|
buf << "State slot " << myCurrentSlot;
|
||||||
myOSystem.frameBuffer().showMessage(buf.str());
|
myOSystem.frameBuffer().showMessage(buf.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#ifndef STATE_MANAGER_HXX
|
#ifndef STATE_MANAGER_HXX
|
||||||
#define STATE_MANAGER_HXX
|
#define STATE_MANAGER_HXX
|
||||||
|
|
||||||
#define STATE_HEADER "06000008state"
|
#define STATE_HEADER "06020100state"
|
||||||
|
|
||||||
class OSystem;
|
class OSystem;
|
||||||
class RewindManager;
|
class RewindManager;
|
||||||
|
@ -115,8 +115,10 @@ class StateManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Switches to the next higher or lower state slot (circular queue style).
|
Switches to the next higher or lower state slot (circular queue style).
|
||||||
|
|
||||||
|
@param direction +1 indicates increase, -1 indicates decrease.
|
||||||
*/
|
*/
|
||||||
void changeState(int direction);
|
void changeState(int direction = +1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Toggles auto slot mode.
|
Toggles auto slot mode.
|
||||||
|
|
|
@ -22,19 +22,19 @@
|
||||||
|
|
||||||
namespace Vec {
|
namespace Vec {
|
||||||
|
|
||||||
template<class T>
|
template<typename T>
|
||||||
void append(vector<T>& dst, const vector<T>& src)
|
void append(vector<T>& dst, const vector<T>& src)
|
||||||
{
|
{
|
||||||
dst.insert(dst.cend(), src.cbegin(), src.cend());
|
dst.insert(dst.cend(), src.cbegin(), src.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<typename T>
|
||||||
void insertAt(vector<T>& dst, uInt32 idx, const T& element)
|
void insertAt(vector<T>& dst, uInt32 idx, const T& element)
|
||||||
{
|
{
|
||||||
dst.insert(dst.cbegin()+idx, element);
|
dst.insert(dst.cbegin()+idx, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<typename T>
|
||||||
void removeAt(vector<T>& dst, uInt32 idx)
|
void removeAt(vector<T>& dst, uInt32 idx)
|
||||||
{
|
{
|
||||||
dst.erase(dst.cbegin()+idx);
|
dst.erase(dst.cbegin()+idx);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#ifndef VERSION_HXX
|
#ifndef VERSION_HXX
|
||||||
#define VERSION_HXX
|
#define VERSION_HXX
|
||||||
|
|
||||||
#define STELLA_VERSION "6.1_rc1"
|
#define STELLA_VERSION "6.3_pre"
|
||||||
#define STELLA_BUILD "5657"
|
#define STELLA_BUILD "6091"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -297,7 +297,7 @@ class ZipHandler
|
||||||
void addToCache();
|
void addToCache();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uInt32 DECOMPRESS_BUFSIZE = 16_KB;
|
static constexpr size_t DECOMPRESS_BUFSIZE = 16_KB;
|
||||||
static constexpr uInt32 CACHE_SIZE = 8; // number of open files to cache
|
static constexpr uInt32 CACHE_SIZE = 8; // number of open files to cache
|
||||||
|
|
||||||
ZipFilePtr myZip;
|
ZipFilePtr myZip;
|
||||||
|
|
|
@ -43,6 +43,7 @@ using uInt64 = uint64_t;
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -63,8 +64,6 @@ using std::istream;
|
||||||
using std::ostream;
|
using std::ostream;
|
||||||
using std::fstream;
|
using std::fstream;
|
||||||
using std::iostream;
|
using std::iostream;
|
||||||
using std::ifstream;
|
|
||||||
using std::ofstream;
|
|
||||||
using std::ostringstream;
|
using std::ostringstream;
|
||||||
using std::istringstream;
|
using std::istringstream;
|
||||||
using std::stringstream;
|
using std::stringstream;
|
||||||
|
@ -84,11 +83,14 @@ using ByteArray = std::vector<uInt8>;
|
||||||
using ShortArray = std::vector<uInt16>;
|
using ShortArray = std::vector<uInt16>;
|
||||||
using StringList = std::vector<std::string>;
|
using StringList = std::vector<std::string>;
|
||||||
using ByteBuffer = std::unique_ptr<uInt8[]>; // NOLINT
|
using ByteBuffer = std::unique_ptr<uInt8[]>; // NOLINT
|
||||||
|
using DWordBuffer = std::unique_ptr<uInt32[]>; // NOLINT
|
||||||
|
|
||||||
|
using AdjustFunction = std::function<void(int)>;
|
||||||
|
|
||||||
// We use KB a lot; let's make a literal for it
|
// We use KB a lot; let's make a literal for it
|
||||||
constexpr uInt32 operator "" _KB(unsigned long long size)
|
constexpr size_t operator "" _KB(unsigned long long size)
|
||||||
{
|
{
|
||||||
return static_cast<uInt32>(size * 1024);
|
return static_cast<size_t>(size * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const string EmptyString("");
|
static const string EmptyString("");
|
||||||
|
@ -97,6 +99,12 @@ static const string EmptyString("");
|
||||||
#undef PAGE_SIZE
|
#undef PAGE_SIZE
|
||||||
#undef PAGE_MASK
|
#undef PAGE_MASK
|
||||||
|
|
||||||
|
// Adaptable refresh is currently not available on MacOS
|
||||||
|
// In the future, this may expand to other systems
|
||||||
|
#if !defined(BSPF_MACOS)
|
||||||
|
#define ADAPTABLE_REFRESH_SUPPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace BSPF
|
namespace BSPF
|
||||||
{
|
{
|
||||||
static constexpr float PI_f = 3.141592653589793238462643383279502884F;
|
static constexpr float PI_f = 3.141592653589793238462643383279502884F;
|
||||||
|
@ -118,20 +126,39 @@ namespace BSPF
|
||||||
static const string ARCH = "NOARCH";
|
static const string ARCH = "NOARCH";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Get next power of two greater than or equal to the given value
|
||||||
|
inline size_t nextPowerOfTwo(size_t size) {
|
||||||
|
if(size < 2) return 1;
|
||||||
|
size_t power2 = 1;
|
||||||
|
while(power2 < size)
|
||||||
|
power2 <<= 1;
|
||||||
|
return power2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get next multiple of the given value
|
||||||
|
// Note that this only works when multiple is a power of two
|
||||||
|
inline size_t nextMultipleOf(size_t size, size_t multiple) {
|
||||||
|
return (size + multiple - 1) & ~(multiple - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Make 2D-arrays using std::array less verbose
|
// Make 2D-arrays using std::array less verbose
|
||||||
template<class T, size_t ROW, size_t COL>
|
template<typename T, size_t ROW, size_t COL>
|
||||||
using array2D = std::array<std::array<T, COL>, ROW>;
|
using array2D = std::array<std::array<T, COL>, ROW>;
|
||||||
|
|
||||||
// Combines 'max' and 'min', and clamps value to the upper/lower value
|
// Combines 'max' and 'min', and clamps value to the upper/lower value
|
||||||
// if it is outside the specified range
|
// if it is outside the specified range
|
||||||
template<class T> inline T clamp(T val, T lower, T upper)
|
template<typename T> inline T clamp(T val, T lower, T upper)
|
||||||
{
|
{
|
||||||
return (val < lower) ? lower : (val > upper) ? upper : val;
|
return (val < lower) ? lower : (val > upper) ? upper : val;
|
||||||
}
|
}
|
||||||
template<class T> inline void clamp(T& val, T lower, T upper, T setVal)
|
template<typename T> inline void clamp(T& val, T lower, T upper, T setVal)
|
||||||
{
|
{
|
||||||
if(val < lower || val > upper) val = setVal;
|
if(val < lower || val > upper) val = setVal;
|
||||||
}
|
}
|
||||||
|
template<typename T> inline T clampw(T val, T lower, T upper)
|
||||||
|
{
|
||||||
|
return (val < lower) ? upper : (val > upper) ? lower : val;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert string to given case
|
// Convert string to given case
|
||||||
inline const string& toUpperCase(string& s)
|
inline const string& toUpperCase(string& s)
|
||||||
|
|
|
@ -12,6 +12,7 @@ MODULE_OBJS := \
|
||||||
src/common/Logger.o \
|
src/common/Logger.o \
|
||||||
src/common/main.o \
|
src/common/main.o \
|
||||||
src/common/MouseControl.o \
|
src/common/MouseControl.o \
|
||||||
|
src/common/PaletteHandler.o \
|
||||||
src/common/PhosphorHandler.o \
|
src/common/PhosphorHandler.o \
|
||||||
src/common/PhysicalJoystick.o \
|
src/common/PhysicalJoystick.o \
|
||||||
src/common/PJoystickHandler.o \
|
src/common/PJoystickHandler.o \
|
||||||
|
|
|
@ -28,8 +28,8 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const string& filename)
|
KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const FilesystemNode& file)
|
||||||
: myFilename(filename)
|
: myFile(file)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,10 +41,14 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
|
||||||
string line, key, value;
|
string line, key, value;
|
||||||
string::size_type equalPos, garbage;
|
string::size_type equalPos, garbage;
|
||||||
|
|
||||||
ifstream in(myFilename);
|
stringstream in;
|
||||||
if(!in || !in.is_open()) {
|
try
|
||||||
Logger::error("ERROR: Couldn't load from settings file " + myFilename);
|
{
|
||||||
|
myFile.read(in);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
Logger::error("ERROR: Couldn't load from settings file " + myFile.getShortPath());
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,13 +83,7 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
|
void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
|
||||||
{
|
{
|
||||||
ofstream out(myFilename);
|
stringstream out;
|
||||||
if(!out || !out.is_open()) {
|
|
||||||
Logger::error("ERROR: Couldn't save to settings file " + myFilename);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
out << "; Stella configuration file" << endl
|
out << "; Stella configuration file" << endl
|
||||||
<< ";" << endl
|
<< ";" << endl
|
||||||
<< "; Lines starting with ';' are comments and are ignored." << endl
|
<< "; Lines starting with ';' are comments and are ignored." << endl
|
||||||
|
@ -104,4 +102,13 @@ void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
|
||||||
// Write out each of the key and value pairs
|
// Write out each of the key and value pairs
|
||||||
for(const auto& pair: values)
|
for(const auto& pair: values)
|
||||||
out << pair.first << " = " << pair.second << endl;
|
out << pair.first << " = " << pair.second << endl;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
myFile.write(out);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
Logger::error("ERROR: Couldn't save to settings file " + myFile.getShortPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
#ifndef KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
#ifndef KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
||||||
#define KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
#define KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
||||||
|
|
||||||
|
#include "FSNode.hxx"
|
||||||
#include "KeyValueRepository.hxx"
|
#include "KeyValueRepository.hxx"
|
||||||
|
|
||||||
class KeyValueRepositoryConfigfile : public KeyValueRepository
|
class KeyValueRepositoryConfigfile : public KeyValueRepository
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit KeyValueRepositoryConfigfile(const string& filename);
|
explicit KeyValueRepositoryConfigfile(const FilesystemNode& file);
|
||||||
|
|
||||||
std::map<string, Variant> load() override;
|
std::map<string, Variant> load() override;
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
const string& myFilename;
|
FilesystemNode myFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
#endif // KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include "BilinearBlitter.hxx"
|
#include "FrameBufferSDL2.hxx"
|
||||||
|
|
||||||
#include "ThreadDebugging.hxx"
|
#include "ThreadDebugging.hxx"
|
||||||
|
#include "BilinearBlitter.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb, bool interpolate)
|
BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb, bool interpolate)
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
#ifndef BILINEAR_BLITTER_HXX
|
#ifndef BILINEAR_BLITTER_HXX
|
||||||
#define BILINEAR_BLITTER_HXX
|
#define BILINEAR_BLITTER_HXX
|
||||||
|
|
||||||
|
class FrameBufferSDL2;
|
||||||
|
|
||||||
#include "Blitter.hxx"
|
#include "Blitter.hxx"
|
||||||
#include "FrameBufferSDL2.hxx"
|
|
||||||
#include "SDL_lib.hxx"
|
#include "SDL_lib.hxx"
|
||||||
|
|
||||||
class BilinearBlitter : public Blitter {
|
class BilinearBlitter : public Blitter {
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include "QisBlitter.hxx"
|
#include "FrameBufferSDL2.hxx"
|
||||||
|
|
||||||
#include "ThreadDebugging.hxx"
|
#include "ThreadDebugging.hxx"
|
||||||
|
#include "QisBlitter.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
QisBlitter::QisBlitter(FrameBufferSDL2& fb)
|
QisBlitter::QisBlitter(FrameBufferSDL2& fb)
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
#ifndef QIS_BLITTER_HXX
|
#ifndef QIS_BLITTER_HXX
|
||||||
#define QIS_BLITTER_HXX
|
#define QIS_BLITTER_HXX
|
||||||
|
|
||||||
|
class FrameBufferSDL2;
|
||||||
|
|
||||||
#include "Blitter.hxx"
|
#include "Blitter.hxx"
|
||||||
#include "FrameBufferSDL2.hxx"
|
|
||||||
#include "SDL_lib.hxx"
|
#include "SDL_lib.hxx"
|
||||||
|
|
||||||
class QisBlitter : public Blitter {
|
class QisBlitter : public Blitter {
|
||||||
|
|
|
@ -57,9 +57,15 @@ void AtariNTSC::generateKernels()
|
||||||
const uInt8* ptr = myRGBPalette.data();
|
const uInt8* ptr = myRGBPalette.data();
|
||||||
for(size_t entry = 0; entry < myRGBPalette.size() / 3; ++entry)
|
for(size_t entry = 0; entry < myRGBPalette.size() / 3; ++entry)
|
||||||
{
|
{
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
float r = myImpl.to_float[*ptr++],
|
float r = myImpl.to_float[*ptr++],
|
||||||
g = myImpl.to_float[*ptr++],
|
g = myImpl.to_float[*ptr++],
|
||||||
b = myImpl.to_float[*ptr++];
|
b = myImpl.to_float[*ptr++];
|
||||||
|
#else
|
||||||
|
float r = (*ptr++) / 255.F * rgb_unit + rgb_offset,
|
||||||
|
g = (*ptr++) / 255.F * rgb_unit + rgb_offset,
|
||||||
|
b = (*ptr++) / 255.F * rgb_unit + rgb_offset;
|
||||||
|
#endif
|
||||||
float y, i, q; RGB_TO_YIQ( r, g, b, y, i, q );
|
float y, i, q; RGB_TO_YIQ( r, g, b, y, i, q );
|
||||||
|
|
||||||
// Generate kernel
|
// Generate kernel
|
||||||
|
@ -319,8 +325,10 @@ void AtariNTSC::renderWithPhosphorThread(const uInt8* atari_in, const uInt32 in_
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void AtariNTSC::init(init_t& impl, const Setup& setup)
|
void AtariNTSC::init(init_t& impl, const Setup& setup)
|
||||||
{
|
{
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
impl.brightness = setup.brightness * (0.5F * rgb_unit) + rgb_offset;
|
impl.brightness = setup.brightness * (0.5F * rgb_unit) + rgb_offset;
|
||||||
impl.contrast = setup.contrast * (0.5F * rgb_unit) + rgb_unit;
|
impl.contrast = setup.contrast * (0.5F * rgb_unit) + rgb_unit;
|
||||||
|
#endif
|
||||||
|
|
||||||
impl.artifacts = setup.artifacts;
|
impl.artifacts = setup.artifacts;
|
||||||
if ( impl.artifacts > 0 )
|
if ( impl.artifacts > 0 )
|
||||||
|
@ -334,6 +342,7 @@ void AtariNTSC::init(init_t& impl, const Setup& setup)
|
||||||
|
|
||||||
initFilters(impl, setup);
|
initFilters(impl, setup);
|
||||||
|
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
/* generate gamma table */
|
/* generate gamma table */
|
||||||
if (true) /* was (gamma_size > 1) */
|
if (true) /* was (gamma_size > 1) */
|
||||||
{
|
{
|
||||||
|
@ -345,15 +354,18 @@ void AtariNTSC::init(init_t& impl, const Setup& setup)
|
||||||
impl.to_float[i] =
|
impl.to_float[i] =
|
||||||
powf(i * to_float, gamma) * impl.contrast + impl.brightness;
|
powf(i * to_float, gamma) * impl.contrast + impl.brightness;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* setup decoder matricies */
|
/* setup decoder matricies */
|
||||||
{
|
{
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
float hue = setup.hue * BSPF::PI_f + BSPF::PI_f / 180 * ext_decoder_hue;
|
float hue = setup.hue * BSPF::PI_f + BSPF::PI_f / 180 * ext_decoder_hue;
|
||||||
float sat = setup.saturation + 1;
|
float sat = setup.saturation + 1;
|
||||||
hue += BSPF::PI_f / 180 * (std_decoder_hue - ext_decoder_hue);
|
hue += BSPF::PI_f / 180 * (std_decoder_hue - ext_decoder_hue);
|
||||||
|
|
||||||
float s = sinf(hue)*sat;
|
float s = sinf(hue)*sat;
|
||||||
float c = cosf(hue)*sat;
|
float c = cosf(hue)*sat;
|
||||||
|
#endif
|
||||||
float* out = impl.to_rgb.data();
|
float* out = impl.to_rgb.data();
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
|
@ -366,8 +378,13 @@ void AtariNTSC::init(init_t& impl, const Setup& setup)
|
||||||
{
|
{
|
||||||
float i = *in++;
|
float i = *in++;
|
||||||
float q = *in++;
|
float q = *in++;
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
*out++ = i * c - q * s;
|
*out++ = i * c - q * s;
|
||||||
*out++ = i * s + q * c;
|
*out++ = i * s + q * c;
|
||||||
|
#else
|
||||||
|
*out++ = i ;
|
||||||
|
*out++ = q;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
while ( --n2 );
|
while ( --n2 );
|
||||||
#if 0 // burst_count is always 0
|
#if 0 // burst_count is always 0
|
||||||
|
@ -544,16 +561,32 @@ void AtariNTSC::genKernel(init_t& impl, float y, float i, float q, uInt32* out)
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
const AtariNTSC::Setup AtariNTSC::TV_Composite = {
|
const AtariNTSC::Setup AtariNTSC::TV_Composite = {
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.15F, 0.0F, 0.0F, 0.0F
|
0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.15F, 0.0F, 0.0F, 0.0F
|
||||||
|
#else
|
||||||
|
0.0F, 0.15F, 0.0F, 0.0F, 0.0F
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
const AtariNTSC::Setup AtariNTSC::TV_SVideo = {
|
const AtariNTSC::Setup AtariNTSC::TV_SVideo = {
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.45F, -1.0F, -1.0F, 0.0F
|
0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.45F, -1.0F, -1.0F, 0.0F
|
||||||
|
#else
|
||||||
|
0.0F, 0.45F, -1.0F, -1.0F, 0.0F
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
const AtariNTSC::Setup AtariNTSC::TV_RGB = {
|
const AtariNTSC::Setup AtariNTSC::TV_RGB = {
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
0.0F, 0.0F, 0.0F, 0.0F, 0.2F, 0.0F, 0.70F, -1.0F, -1.0F, -1.0F
|
0.0F, 0.0F, 0.0F, 0.0F, 0.2F, 0.0F, 0.70F, -1.0F, -1.0F, -1.0F
|
||||||
|
#else
|
||||||
|
0.2F, 0.70F, -1.0F, -1.0F, -1.0F
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
const AtariNTSC::Setup AtariNTSC::TV_Bad = {
|
const AtariNTSC::Setup AtariNTSC::TV_Bad = {
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
0.1F, -0.3F, 0.3F, 0.25F, 0.2F, 0.0F, 0.1F, 0.5F, 0.5F, 0.5F
|
0.1F, -0.3F, 0.3F, 0.25F, 0.2F, 0.0F, 0.1F, 0.5F, 0.5F, 0.5F
|
||||||
|
#else
|
||||||
|
0.2F, 0.1F, 0.5F, 0.5F, 0.5F
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
#include "FrameBufferConstants.hxx"
|
#include "FrameBufferConstants.hxx"
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
|
|
||||||
|
//#define BLARGG_PALETTE // also modify contrast, brightness, saturation, gamma and hue when defined
|
||||||
|
|
||||||
class AtariNTSC
|
class AtariNTSC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -57,14 +59,18 @@ class AtariNTSC
|
||||||
struct Setup
|
struct Setup
|
||||||
{
|
{
|
||||||
// Basic parameters
|
// Basic parameters
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
float hue{0.F}; // -1 = -180 degrees +1 = +180 degrees
|
float hue{0.F}; // -1 = -180 degrees +1 = +180 degrees
|
||||||
float saturation{0.F}; // -1 = grayscale (0.0) +1 = oversaturated colors (2.0)
|
float saturation{0.F}; // -1 = grayscale (0.0) +1 = oversaturated colors (2.0)
|
||||||
float contrast{0.F}; // -1 = dark (0.5) +1 = light (1.5)
|
float contrast{0.F}; // -1 = dark (0.5) +1 = light (1.5)
|
||||||
float brightness{0.F}; // -1 = dark (0.5) +1 = light (1.5)
|
float brightness{0.F}; // -1 = dark (0.5) +1 = light (1.5)
|
||||||
|
#endif
|
||||||
float sharpness{0.F}; // edge contrast enhancement/blurring
|
float sharpness{0.F}; // edge contrast enhancement/blurring
|
||||||
|
|
||||||
// Advanced parameters
|
// Advanced parameters
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
float gamma{0.F}; // -1 = dark (1.5) +1 = light (0.5)
|
float gamma{0.F}; // -1 = dark (1.5) +1 = light (0.5)
|
||||||
|
#endif
|
||||||
float resolution{0.F}; // image resolution
|
float resolution{0.F}; // image resolution
|
||||||
float artifacts{0.F}; // artifacts caused by color changes
|
float artifacts{0.F}; // artifacts caused by color changes
|
||||||
float fringing{0.F}; // color artifacts caused by brightness changes
|
float fringing{0.F}; // color artifacts caused by brightness changes
|
||||||
|
@ -127,7 +133,9 @@ class AtariNTSC
|
||||||
burst_size = entry_size / burst_count,
|
burst_size = entry_size / burst_count,
|
||||||
kernel_half = 16,
|
kernel_half = 16,
|
||||||
kernel_size = kernel_half * 2 + 1,
|
kernel_size = kernel_half * 2 + 1,
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
gamma_size = 256,
|
gamma_size = 256,
|
||||||
|
#endif
|
||||||
|
|
||||||
rgb_builder = ((1 << 21) | (1 << 11) | (1 << 1)),
|
rgb_builder = ((1 << 21) | (1 << 11) | (1 << 1)),
|
||||||
rgb_kernel_size = burst_size / alignment_count,
|
rgb_kernel_size = burst_size / alignment_count,
|
||||||
|
@ -162,16 +170,20 @@ class AtariNTSC
|
||||||
struct init_t
|
struct init_t
|
||||||
{
|
{
|
||||||
std::array<float, burst_count * 6> to_rgb{0.F};
|
std::array<float, burst_count * 6> to_rgb{0.F};
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
std::array<float, gamma_size> to_float{0.F};
|
std::array<float, gamma_size> to_float{0.F};
|
||||||
float contrast{0.F};
|
float contrast{0.F};
|
||||||
float brightness{0.F};
|
float brightness{0.F};
|
||||||
|
#endif
|
||||||
float artifacts{0.F};
|
float artifacts{0.F};
|
||||||
float fringing{0.F};
|
float fringing{0.F};
|
||||||
std::array<float, rescale_out * kernel_size * 2> kernel{0.F};
|
std::array<float, rescale_out * kernel_size * 2> kernel{0.F};
|
||||||
|
|
||||||
init_t() {
|
init_t() {
|
||||||
to_rgb.fill(0.0);
|
to_rgb.fill(0.0);
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
to_float.fill(0.0);
|
to_float.fill(0.0);
|
||||||
|
#endif
|
||||||
kernel.fill(0.0);
|
kernel.fill(0.0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include "NTSCFilter.hxx"
|
#include "NTSCFilter.hxx"
|
||||||
|
|
||||||
constexpr float scaleFrom100(float x) { return (x / 50.F) - 1.F; }
|
constexpr float scaleFrom100(float x) { return (x / 50.F) - 1.F; }
|
||||||
constexpr uInt32 scaleTo100(float x) { return uInt32(50*(x+1.F)); }
|
constexpr uInt32 scaleTo100(float x) { return uInt32(50.0001F * (x + 1.F)); }
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string NTSCFilter::setPreset(Preset preset)
|
string NTSCFilter::setPreset(Preset preset)
|
||||||
|
@ -62,9 +62,9 @@ string NTSCFilter::getPreset() const
|
||||||
{
|
{
|
||||||
switch(myPreset)
|
switch(myPreset)
|
||||||
{
|
{
|
||||||
case Preset::COMPOSITE: return "COMPOSITE";
|
|
||||||
case Preset::SVIDEO: return "S-VIDEO";
|
|
||||||
case Preset::RGB: return "RGB";
|
case Preset::RGB: return "RGB";
|
||||||
|
case Preset::SVIDEO: return "S-VIDEO";
|
||||||
|
case Preset::COMPOSITE: return "COMPOSITE";
|
||||||
case Preset::BAD: return "BAD ADJUST";
|
case Preset::BAD: return "BAD ADJUST";
|
||||||
case Preset::CUSTOM: return "CUSTOM";
|
case Preset::CUSTOM: return "CUSTOM";
|
||||||
default: return "Disabled";
|
default: return "Disabled";
|
||||||
|
@ -72,81 +72,81 @@ string NTSCFilter::getPreset() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string NTSCFilter::setNextAdjustable()
|
void NTSCFilter::selectAdjustable(int direction,
|
||||||
|
string& text, string& valueText, Int32& value)
|
||||||
{
|
{
|
||||||
if(myPreset != Preset::CUSTOM)
|
if(direction == +1)
|
||||||
return "'Custom' TV mode not selected";
|
{
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
myCurrentAdjustable = (myCurrentAdjustable + 1) % 10;
|
myCurrentAdjustable = (myCurrentAdjustable + 1) % 10;
|
||||||
ostringstream buf;
|
#else
|
||||||
buf << "Custom adjustable '" << ourCustomAdjustables[myCurrentAdjustable].type
|
myCurrentAdjustable = (myCurrentAdjustable + 1) % 5;
|
||||||
<< "' selected";
|
#endif
|
||||||
|
|
||||||
return buf.str();
|
|
||||||
}
|
}
|
||||||
|
else if(direction == -1)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
string NTSCFilter::setPreviousAdjustable()
|
|
||||||
{
|
{
|
||||||
if(myPreset != Preset::CUSTOM)
|
#ifdef BLARGG_PALETTE
|
||||||
return "'Custom' TV mode not selected";
|
|
||||||
|
|
||||||
if(myCurrentAdjustable == 0) myCurrentAdjustable = 9;
|
if(myCurrentAdjustable == 0) myCurrentAdjustable = 9;
|
||||||
|
#else
|
||||||
|
if(myCurrentAdjustable == 0) myCurrentAdjustable = 4;
|
||||||
|
#endif
|
||||||
else --myCurrentAdjustable;
|
else --myCurrentAdjustable;
|
||||||
ostringstream buf;
|
}
|
||||||
buf << "Custom adjustable '" << ourCustomAdjustables[myCurrentAdjustable].type
|
|
||||||
<< "' selected";
|
|
||||||
|
|
||||||
return buf.str();
|
ostringstream msg, val;
|
||||||
|
|
||||||
|
value = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
||||||
|
msg << "Custom " << ourCustomAdjustables[myCurrentAdjustable].type;
|
||||||
|
val << value << "%";
|
||||||
|
|
||||||
|
text = msg.str();
|
||||||
|
valueText = val.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string NTSCFilter::increaseAdjustable()
|
void NTSCFilter::changeAdjustable(int adjustable, int direction,
|
||||||
|
string& text, string& valueText, Int32& newValue)
|
||||||
{
|
{
|
||||||
if(myPreset != Preset::CUSTOM)
|
myCurrentAdjustable = adjustable;
|
||||||
return "'Custom' TV mode not selected";
|
changeCurrentAdjustable(direction, text, valueText, newValue);
|
||||||
|
|
||||||
uInt32 newval = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
|
||||||
newval += 2; if(newval > 100) newval = 100;
|
|
||||||
*ourCustomAdjustables[myCurrentAdjustable].value = scaleFrom100(newval);
|
|
||||||
|
|
||||||
ostringstream buf;
|
|
||||||
buf << "Custom '" << ourCustomAdjustables[myCurrentAdjustable].type
|
|
||||||
<< "' set to " << newval;
|
|
||||||
|
|
||||||
setPreset(myPreset);
|
|
||||||
return buf.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string NTSCFilter::decreaseAdjustable()
|
void NTSCFilter::changeCurrentAdjustable(int direction,
|
||||||
|
string& text, string& valueText, Int32& newValue)
|
||||||
{
|
{
|
||||||
if(myPreset != Preset::CUSTOM)
|
//if(myPreset != Preset::CUSTOM)
|
||||||
return "'Custom' TV mode not selected";
|
// return "'Custom' TV mode not selected";
|
||||||
|
|
||||||
uInt32 newval = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
newValue = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
||||||
if(newval < 2) newval = 0;
|
newValue = BSPF::clamp(newValue + direction * 1, 0, 100);
|
||||||
else newval -= 2;
|
|
||||||
*ourCustomAdjustables[myCurrentAdjustable].value = scaleFrom100(newval);
|
|
||||||
|
|
||||||
ostringstream buf;
|
*ourCustomAdjustables[myCurrentAdjustable].value = scaleFrom100(newValue);
|
||||||
buf << "Custom '" << ourCustomAdjustables[myCurrentAdjustable].type
|
|
||||||
<< "' set to " << newval;
|
|
||||||
|
|
||||||
setPreset(myPreset);
|
setPreset(myPreset);
|
||||||
return buf.str();
|
|
||||||
|
ostringstream msg, val;
|
||||||
|
|
||||||
|
msg << "Custom " << ourCustomAdjustables[myCurrentAdjustable].type;
|
||||||
|
val << newValue << "%";
|
||||||
|
|
||||||
|
text = msg.str();
|
||||||
|
valueText = val.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void NTSCFilter::loadConfig(const Settings& settings)
|
void NTSCFilter::loadConfig(const Settings& settings)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Load adjustables for custom mode
|
// Load adjustables for custom mode
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
myCustomSetup.hue = BSPF::clamp(settings.getFloat("tv.hue"), -1.0F, 1.0F);
|
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.saturation = BSPF::clamp(settings.getFloat("tv.saturation"), -1.0F, 1.0F);
|
||||||
myCustomSetup.contrast = BSPF::clamp(settings.getFloat("tv.contrast"), -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.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.gamma = BSPF::clamp(settings.getFloat("tv.gamma"), -1.0F, 1.0F);
|
||||||
|
#endif
|
||||||
|
myCustomSetup.sharpness = BSPF::clamp(settings.getFloat("tv.sharpness"), -1.0F, 1.0F);
|
||||||
myCustomSetup.resolution = BSPF::clamp(settings.getFloat("tv.resolution"), -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.artifacts = BSPF::clamp(settings.getFloat("tv.artifacts"), -1.0F, 1.0F);
|
||||||
myCustomSetup.fringing = BSPF::clamp(settings.getFloat("tv.fringing"), -1.0F, 1.0F);
|
myCustomSetup.fringing = BSPF::clamp(settings.getFloat("tv.fringing"), -1.0F, 1.0F);
|
||||||
|
@ -157,12 +157,14 @@ void NTSCFilter::loadConfig(const Settings& settings)
|
||||||
void NTSCFilter::saveConfig(Settings& settings) const
|
void NTSCFilter::saveConfig(Settings& settings) const
|
||||||
{
|
{
|
||||||
// Save adjustables for custom mode
|
// Save adjustables for custom mode
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
settings.setValue("tv.hue", myCustomSetup.hue);
|
settings.setValue("tv.hue", myCustomSetup.hue);
|
||||||
settings.setValue("tv.saturation", myCustomSetup.saturation);
|
settings.setValue("tv.saturation", myCustomSetup.saturation);
|
||||||
settings.setValue("tv.contrast", myCustomSetup.contrast);
|
settings.setValue("tv.contrast", myCustomSetup.contrast);
|
||||||
settings.setValue("tv.brightness", myCustomSetup.brightness);
|
settings.setValue("tv.brightness", myCustomSetup.brightness);
|
||||||
settings.setValue("tv.sharpness", myCustomSetup.sharpness);
|
|
||||||
settings.setValue("tv.gamma", myCustomSetup.gamma);
|
settings.setValue("tv.gamma", myCustomSetup.gamma);
|
||||||
|
#endif
|
||||||
|
settings.setValue("tv.sharpness", myCustomSetup.sharpness);
|
||||||
settings.setValue("tv.resolution", myCustomSetup.resolution);
|
settings.setValue("tv.resolution", myCustomSetup.resolution);
|
||||||
settings.setValue("tv.artifacts", myCustomSetup.artifacts);
|
settings.setValue("tv.artifacts", myCustomSetup.artifacts);
|
||||||
settings.setValue("tv.fringing", myCustomSetup.fringing);
|
settings.setValue("tv.fringing", myCustomSetup.fringing);
|
||||||
|
@ -174,12 +176,12 @@ void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset) const
|
||||||
{
|
{
|
||||||
switch(preset)
|
switch(preset)
|
||||||
{
|
{
|
||||||
case Preset::COMPOSITE:
|
|
||||||
convertToAdjustable(adjustable, AtariNTSC::TV_Composite); break;
|
|
||||||
case Preset::SVIDEO:
|
|
||||||
convertToAdjustable(adjustable, AtariNTSC::TV_SVideo); break;
|
|
||||||
case Preset::RGB:
|
case Preset::RGB:
|
||||||
convertToAdjustable(adjustable, AtariNTSC::TV_RGB); break;
|
convertToAdjustable(adjustable, AtariNTSC::TV_RGB); break;
|
||||||
|
case Preset::SVIDEO:
|
||||||
|
convertToAdjustable(adjustable, AtariNTSC::TV_SVideo); break;
|
||||||
|
case Preset::COMPOSITE:
|
||||||
|
convertToAdjustable(adjustable, AtariNTSC::TV_Composite); break;
|
||||||
case Preset::BAD:
|
case Preset::BAD:
|
||||||
convertToAdjustable(adjustable, AtariNTSC::TV_Bad); break;
|
convertToAdjustable(adjustable, AtariNTSC::TV_Bad); break;
|
||||||
case Preset::CUSTOM:
|
case Preset::CUSTOM:
|
||||||
|
@ -192,12 +194,14 @@ void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset) const
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void NTSCFilter::setCustomAdjustables(Adjustable& adjustable)
|
void NTSCFilter::setCustomAdjustables(Adjustable& adjustable)
|
||||||
{
|
{
|
||||||
myCustomSetup.hue = scaleFrom100(adjustable.hue);
|
#ifdef BLARGG_PALETTE
|
||||||
myCustomSetup.saturation = scaleFrom100(adjustable.saturation);
|
//myCustomSetup.hue = scaleFrom100(adjustable.hue);
|
||||||
myCustomSetup.contrast = scaleFrom100(adjustable.contrast);
|
//myCustomSetup.saturation = scaleFrom100(adjustable.saturation);
|
||||||
myCustomSetup.brightness = scaleFrom100(adjustable.brightness);
|
//myCustomSetup.contrast = scaleFrom100(adjustable.contrast);
|
||||||
|
//myCustomSetup.brightness = scaleFrom100(adjustable.brightness);
|
||||||
|
//myCustomSetup.gamma = scaleFrom100(adjustable.gamma);
|
||||||
|
#endif
|
||||||
myCustomSetup.sharpness = scaleFrom100(adjustable.sharpness);
|
myCustomSetup.sharpness = scaleFrom100(adjustable.sharpness);
|
||||||
myCustomSetup.gamma = scaleFrom100(adjustable.gamma);
|
|
||||||
myCustomSetup.resolution = scaleFrom100(adjustable.resolution);
|
myCustomSetup.resolution = scaleFrom100(adjustable.resolution);
|
||||||
myCustomSetup.artifacts = scaleFrom100(adjustable.artifacts);
|
myCustomSetup.artifacts = scaleFrom100(adjustable.artifacts);
|
||||||
myCustomSetup.fringing = scaleFrom100(adjustable.fringing);
|
myCustomSetup.fringing = scaleFrom100(adjustable.fringing);
|
||||||
|
@ -208,12 +212,14 @@ void NTSCFilter::setCustomAdjustables(Adjustable& adjustable)
|
||||||
void NTSCFilter::convertToAdjustable(Adjustable& adjustable,
|
void NTSCFilter::convertToAdjustable(Adjustable& adjustable,
|
||||||
const AtariNTSC::Setup& setup) const
|
const AtariNTSC::Setup& setup) const
|
||||||
{
|
{
|
||||||
adjustable.hue = scaleTo100(setup.hue);
|
#ifdef BLARGG_PALETTE
|
||||||
adjustable.saturation = scaleTo100(setup.saturation);
|
//adjustable.hue = scaleTo100(setup.hue);
|
||||||
adjustable.contrast = scaleTo100(setup.contrast);
|
//adjustable.saturation = scaleTo100(setup.saturation);
|
||||||
adjustable.brightness = scaleTo100(setup.brightness);
|
//adjustable.contrast = scaleTo100(setup.contrast);
|
||||||
|
//adjustable.brightness = scaleTo100(setup.brightness);
|
||||||
|
//adjustable.gamma = scaleTo100(setup.gamma);
|
||||||
|
#endif
|
||||||
adjustable.sharpness = scaleTo100(setup.sharpness);
|
adjustable.sharpness = scaleTo100(setup.sharpness);
|
||||||
adjustable.gamma = scaleTo100(setup.gamma);
|
|
||||||
adjustable.resolution = scaleTo100(setup.resolution);
|
adjustable.resolution = scaleTo100(setup.resolution);
|
||||||
adjustable.artifacts = scaleTo100(setup.artifacts);
|
adjustable.artifacts = scaleTo100(setup.artifacts);
|
||||||
adjustable.fringing = scaleTo100(setup.fringing);
|
adjustable.fringing = scaleTo100(setup.fringing);
|
||||||
|
@ -224,12 +230,16 @@ void NTSCFilter::convertToAdjustable(Adjustable& adjustable,
|
||||||
AtariNTSC::Setup NTSCFilter::myCustomSetup = AtariNTSC::TV_Composite;
|
AtariNTSC::Setup NTSCFilter::myCustomSetup = AtariNTSC::TV_Composite;
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
const std::array<NTSCFilter::AdjustableTag, 10> NTSCFilter::ourCustomAdjustables = { {
|
const std::array<NTSCFilter::AdjustableTag, 10> NTSCFilter::ourCustomAdjustables = { {
|
||||||
{ "contrast", &myCustomSetup.contrast },
|
{ "contrast", &myCustomSetup.contrast },
|
||||||
{ "brightness", &myCustomSetup.brightness },
|
{ "brightness", &myCustomSetup.brightness },
|
||||||
{ "hue", &myCustomSetup.hue },
|
{ "hue", &myCustomSetup.hue },
|
||||||
{ "saturation", &myCustomSetup.saturation },
|
{ "saturation", &myCustomSetup.saturation },
|
||||||
{ "gamma", &myCustomSetup.gamma },
|
{ "gamma", &myCustomSetup.gamma },
|
||||||
|
#else
|
||||||
|
const std::array<NTSCFilter::AdjustableTag, int(NTSCFilter::Adjustables::NUM_ADJUSTABLES)> NTSCFilter::ourCustomAdjustables = { {
|
||||||
|
#endif
|
||||||
{ "sharpness", &myCustomSetup.sharpness },
|
{ "sharpness", &myCustomSetup.sharpness },
|
||||||
{ "resolution", &myCustomSetup.resolution },
|
{ "resolution", &myCustomSetup.resolution },
|
||||||
{ "artifacts", &myCustomSetup.artifacts },
|
{ "artifacts", &myCustomSetup.artifacts },
|
||||||
|
|
|
@ -47,12 +47,24 @@ class NTSCFilter
|
||||||
BAD,
|
BAD,
|
||||||
CUSTOM
|
CUSTOM
|
||||||
};
|
};
|
||||||
|
enum class Adjustables {
|
||||||
|
SHARPNESS,
|
||||||
|
RESOLUTION,
|
||||||
|
ARTIFACTS,
|
||||||
|
FRINGING,
|
||||||
|
BLEEDING,
|
||||||
|
NUM_ADJUSTABLES
|
||||||
|
};
|
||||||
|
|
||||||
/* Normally used in conjunction with custom mode, contains all
|
/* Normally used in conjunction with custom mode, contains all
|
||||||
aspects currently adjustable in NTSC TV emulation. */
|
aspects currently adjustable in NTSC TV emulation. */
|
||||||
struct Adjustable {
|
struct Adjustable {
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
uInt32 hue, saturation, contrast, brightness, gamma,
|
uInt32 hue, saturation, contrast, brightness, gamma,
|
||||||
sharpness, resolution, artifacts, fringing, bleed;
|
sharpness, resolution, artifacts, fringing, bleed;
|
||||||
|
#else
|
||||||
|
uInt32 sharpness, resolution, artifacts, fringing, bleed;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -86,10 +98,12 @@ class NTSCFilter
|
||||||
// Changes are made this way since otherwise 20 key-combinations
|
// Changes are made this way since otherwise 20 key-combinations
|
||||||
// would be needed to dynamically change each setting, and now
|
// would be needed to dynamically change each setting, and now
|
||||||
// only 4 combinations are necessary
|
// only 4 combinations are necessary
|
||||||
string setNextAdjustable();
|
void selectAdjustable(int direction,
|
||||||
string setPreviousAdjustable();
|
string& text, string& valueText, Int32& value);
|
||||||
string increaseAdjustable();
|
void changeAdjustable(int adjustable, int direction,
|
||||||
string decreaseAdjustable();
|
string& text, string& valueText, Int32& newValue);
|
||||||
|
void changeCurrentAdjustable(int direction,
|
||||||
|
string& text, string& valueText, Int32& newValue);
|
||||||
|
|
||||||
// Load and save NTSC-related settings
|
// Load and save NTSC-related settings
|
||||||
void loadConfig(const Settings& settings);
|
void loadConfig(const Settings& settings);
|
||||||
|
@ -139,7 +153,11 @@ class NTSCFilter
|
||||||
float* value{nullptr};
|
float* value{nullptr};
|
||||||
};
|
};
|
||||||
uInt32 myCurrentAdjustable{0};
|
uInt32 myCurrentAdjustable{0};
|
||||||
|
#ifdef BLARGG_PALETTE
|
||||||
static const std::array<AdjustableTag, 10> ourCustomAdjustables;
|
static const std::array<AdjustableTag, 10> ourCustomAdjustables;
|
||||||
|
#else
|
||||||
|
static const std::array<AdjustableTag, 5> ourCustomAdjustables;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
|
|
|
@ -32,7 +32,10 @@
|
||||||
#include "CartRamWidget.hxx"
|
#include "CartRamWidget.hxx"
|
||||||
#include "RomWidget.hxx"
|
#include "RomWidget.hxx"
|
||||||
#include "Base.hxx"
|
#include "Base.hxx"
|
||||||
|
#include "Device.hxx"
|
||||||
#include "exception/EmulationWarning.hxx"
|
#include "exception/EmulationWarning.hxx"
|
||||||
|
#include "TIA.hxx"
|
||||||
|
#include "M6532.hxx"
|
||||||
|
|
||||||
using Common::Base;
|
using Common::Base;
|
||||||
using std::hex;
|
using std::hex;
|
||||||
|
@ -68,15 +71,18 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create bank information for each potential bank, and an extra one for ZP RAM
|
// Create bank information for each potential bank, and an extra one for ZP RAM
|
||||||
// Banksizes greater than 4096 indicate multi-bank ROMs, but we handle only
|
// ROM sizes greater than 4096 indicate multi-bank ROMs, but we handle only
|
||||||
// 4K pieces at a time
|
// 4K pieces at a time
|
||||||
// Banksizes less than 4K use the actual value
|
// ROM sizes less than 4K use the actual value
|
||||||
size_t banksize = 0;
|
size_t romSize = 0;
|
||||||
myConsole.cartridge().getImage(banksize);
|
myConsole.cartridge().getImage(romSize);
|
||||||
|
|
||||||
BankInfo info;
|
BankInfo info;
|
||||||
info.size = std::min<size_t>(banksize, 4_KB);
|
info.size = std::min<size_t>(romSize, 4_KB);
|
||||||
for(uInt32 i = 0; i < myConsole.cartridge().bankCount(); ++i)
|
for(uInt32 i = 0; i < myConsole.cartridge().romBankCount(); ++i)
|
||||||
|
myBankInfo.push_back(info);
|
||||||
|
|
||||||
|
for(uInt32 i = 0; i < myConsole.cartridge().ramBankCount(); ++i)
|
||||||
myBankInfo.push_back(info);
|
myBankInfo.push_back(info);
|
||||||
|
|
||||||
info.size = 128; // ZP RAM
|
info.size = 128; // ZP RAM
|
||||||
|
@ -85,7 +91,7 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem)
|
||||||
// We know the address for the startup bank right now
|
// We know the address for the startup bank right now
|
||||||
myBankInfo[myConsole.cartridge().startBank()].addressList.push_front(
|
myBankInfo[myConsole.cartridge().startBank()].addressList.push_front(
|
||||||
myDebugger.dpeek(0xfffc));
|
myDebugger.dpeek(0xfffc));
|
||||||
addLabel("Start", myDebugger.dpeek(0xfffc, DATA));
|
addLabel("Start", myDebugger.dpeek(0xfffc, Device::DATA)); // TOOD: ::CODE???
|
||||||
|
|
||||||
// Add system equates
|
// Add system equates
|
||||||
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
|
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
|
||||||
|
@ -156,6 +162,18 @@ void CartDebug::saveOldState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
int CartDebug::lastReadAddress()
|
||||||
|
{
|
||||||
|
return mySystem.m6502().lastReadAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
int CartDebug::lastWriteAddress()
|
||||||
|
{
|
||||||
|
return mySystem.m6502().lastWriteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int CartDebug::lastReadBaseAddress()
|
int CartDebug::lastReadBaseAddress()
|
||||||
{
|
{
|
||||||
|
@ -224,12 +242,36 @@ string CartDebug::toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartDebug::disassemble(bool force)
|
bool CartDebug::disassembleAddr(uInt16 address, bool force)
|
||||||
|
{
|
||||||
|
// ROM/RAM bank or ZP-RAM?
|
||||||
|
int bank = (address & 0x1000) ? getBank(address) : int(myBankInfo.size()) - 1;
|
||||||
|
|
||||||
|
return disassemble(bank, address, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool CartDebug::disassemblePC(bool force)
|
||||||
|
{
|
||||||
|
return (disassembleAddr(myDebugger.cpuDebug().pc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool CartDebug::disassembleBank(int bank)
|
||||||
|
{
|
||||||
|
BankInfo& info = myBankInfo[bank];
|
||||||
|
|
||||||
|
info.offset = myConsole.cartridge().bankOrigin(bank);
|
||||||
|
|
||||||
|
return disassemble(bank, info.offset, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool CartDebug::disassemble(int bank, uInt16 PC, bool force)
|
||||||
{
|
{
|
||||||
// Test current disassembly; don't re-disassemble if it hasn't changed
|
// Test current disassembly; don't re-disassemble if it hasn't changed
|
||||||
// Also check if the current PC is in the current list
|
// Also check if the current PC is in the current list
|
||||||
bool bankChanged = myConsole.cartridge().bankChanged();
|
bool bankChanged = myConsole.cartridge().bankChanged();
|
||||||
uInt16 PC = myDebugger.cpuDebug().pc();
|
|
||||||
int pcline = addressToLine(PC);
|
int pcline = addressToLine(PC);
|
||||||
bool pcfound = (pcline != -1) && (uInt32(pcline) < myDisassembly.list.size()) &&
|
bool pcfound = (pcline != -1) && (uInt32(pcline) < myDisassembly.list.size()) &&
|
||||||
(myDisassembly.list[pcline].disasm[0] != '.');
|
(myDisassembly.list[pcline].disasm[0] != '.');
|
||||||
|
@ -241,8 +283,9 @@ bool CartDebug::disassemble(bool force)
|
||||||
if(changed)
|
if(changed)
|
||||||
{
|
{
|
||||||
// Are we disassembling from ROM or ZP RAM?
|
// Are we disassembling from ROM or ZP RAM?
|
||||||
BankInfo& info = (PC & 0x1000) ? myBankInfo[getBank(PC)] :
|
BankInfo& info = myBankInfo[bank];
|
||||||
myBankInfo[myBankInfo.size()-1];
|
//(PC & 0x1000) ? myBankInfo[getBank(PC)] :
|
||||||
|
//myBankInfo[myBankInfo.size()-1];
|
||||||
|
|
||||||
// If the offset has changed, all old addresses must be 'converted'
|
// If the offset has changed, all old addresses must be 'converted'
|
||||||
// For example, if the list contains any $fxxx and the address space is now
|
// For example, if the list contains any $fxxx and the address space is now
|
||||||
|
@ -304,8 +347,8 @@ bool CartDebug::fillDisassemblyList(BankInfo& info, uInt16 search)
|
||||||
const DisassemblyTag& tag = myDisassembly.list[i];
|
const DisassemblyTag& tag = myDisassembly.list[i];
|
||||||
const uInt16 address = tag.address & 0xFFF;
|
const uInt16 address = tag.address & 0xFFF;
|
||||||
|
|
||||||
// Exclude 'ROW'; they don't have a valid address
|
// Exclude 'Device::ROW'; they don't have a valid address
|
||||||
if(tag.type != CartDebug::ROW)
|
if(tag.type != Device::ROW)
|
||||||
{
|
{
|
||||||
// Create a mapping from addresses to line numbers
|
// Create a mapping from addresses to line numbers
|
||||||
myAddrToLineList.emplace(address, i);
|
myAddrToLineList.emplace(address, i);
|
||||||
|
@ -331,7 +374,7 @@ int CartDebug::addressToLine(uInt16 address) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
string CartDebug::disassembleLines(uInt16 start, uInt16 lines) const
|
||||||
{
|
{
|
||||||
// Fill the string with disassembled data
|
// Fill the string with disassembled data
|
||||||
start &= 0xFFF;
|
start &= 0xFFF;
|
||||||
|
@ -346,7 +389,7 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
||||||
if((tag.address & 0xfff) >= start)
|
if((tag.address & 0xfff) >= start)
|
||||||
{
|
{
|
||||||
if(begin == list_size) begin = end;
|
if(begin == list_size) begin = end;
|
||||||
if(tag.type != CartDebug::ROW)
|
if(tag.type != Device::ROW)
|
||||||
length = std::max(length, uInt32(tag.disasm.length()));
|
length = std::max(length, uInt32(tag.disasm.length()));
|
||||||
|
|
||||||
--lines;
|
--lines;
|
||||||
|
@ -357,7 +400,7 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
||||||
for(uInt32 i = begin; i < end; ++i)
|
for(uInt32 i = begin; i < end; ++i)
|
||||||
{
|
{
|
||||||
const CartDebug::DisassemblyTag& tag = myDisassembly.list[i];
|
const CartDebug::DisassemblyTag& tag = myDisassembly.list[i];
|
||||||
if(tag.type == CartDebug::NONE)
|
if(tag.type == Device::NONE)
|
||||||
continue;
|
continue;
|
||||||
else if(tag.address)
|
else if(tag.address)
|
||||||
buffer << std::uppercase << std::hex << std::setw(4)
|
buffer << std::uppercase << std::hex << std::setw(4)
|
||||||
|
@ -374,7 +417,7 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartDebug::addDirective(CartDebug::DisasmType type,
|
bool CartDebug::addDirective(Device::AccessType type,
|
||||||
uInt16 start, uInt16 end, int bank)
|
uInt16 start, uInt16 end, int bank)
|
||||||
{
|
{
|
||||||
if(end < start || start == 0 || end == 0)
|
if(end < start || start == 0 || end == 0)
|
||||||
|
@ -384,7 +427,7 @@ bool CartDebug::addDirective(CartDebug::DisasmType type,
|
||||||
bank = (myDebugger.cpuDebug().pc() & 0x1000) ?
|
bank = (myDebugger.cpuDebug().pc() & 0x1000) ?
|
||||||
getBank(myDebugger.cpuDebug().pc()) : int(myBankInfo.size())-1;
|
getBank(myDebugger.cpuDebug().pc()) : int(myBankInfo.size())-1;
|
||||||
|
|
||||||
bank = std::min(bank, bankCount());
|
bank = std::min(bank, romBankCount());
|
||||||
BankInfo& info = myBankInfo[bank];
|
BankInfo& info = myBankInfo[bank];
|
||||||
DirectiveList& list = info.directiveList;
|
DirectiveList& list = info.directiveList;
|
||||||
|
|
||||||
|
@ -516,9 +559,9 @@ int CartDebug::getPCBank()
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int CartDebug::bankCount() const
|
int CartDebug::romBankCount() const
|
||||||
{
|
{
|
||||||
return myConsole.cartridge().bankCount();
|
return myConsole.cartridge().romBankCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -696,19 +739,20 @@ string CartDebug::loadListFile()
|
||||||
// The default naming/location for list files is the ROM dir based on the
|
// The default naming/location for list files is the ROM dir based on the
|
||||||
// actual ROM filename
|
// actual ROM filename
|
||||||
|
|
||||||
if(myListFile == "")
|
FilesystemNode lst(myOSystem.romFile().getPathWithExt(".lst"));
|
||||||
{
|
if(!lst.isReadable())
|
||||||
FilesystemNode lst(myOSystem.romFile().getPathWithExt("") + ".lst");
|
|
||||||
if(lst.isFile() && lst.isReadable())
|
|
||||||
myListFile = lst.getPath();
|
|
||||||
else
|
|
||||||
return DebuggerParser::red("list file \'" + lst.getShortPath() + "\' not found");
|
return DebuggerParser::red("list file \'" + lst.getShortPath() + "\' not found");
|
||||||
}
|
|
||||||
|
|
||||||
FilesystemNode node(myListFile);
|
stringstream in;
|
||||||
ifstream in(node.getPath());
|
try
|
||||||
if(!in.is_open())
|
{
|
||||||
return DebuggerParser::red("list file '" + node.getShortPath() + "' not readable");
|
if(lst.read(in) == 0)
|
||||||
|
return DebuggerParser::red("list file '" + lst.getShortPath() + "' not found");
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return DebuggerParser::red("list file '" + lst.getShortPath() + "' not readable");
|
||||||
|
}
|
||||||
|
|
||||||
while(!in.eof())
|
while(!in.eof())
|
||||||
{
|
{
|
||||||
|
@ -747,7 +791,7 @@ string CartDebug::loadListFile()
|
||||||
}
|
}
|
||||||
myDebugger.rom().invalidate();
|
myDebugger.rom().invalidate();
|
||||||
|
|
||||||
return "list file '" + node.getShortPath() + "' loaded OK";
|
return "list file '" + lst.getShortPath() + "' loaded OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -756,23 +800,24 @@ string CartDebug::loadSymbolFile()
|
||||||
// The default naming/location for symbol files is the ROM dir based on the
|
// The default naming/location for symbol files is the ROM dir based on the
|
||||||
// actual ROM filename
|
// actual ROM filename
|
||||||
|
|
||||||
if(mySymbolFile == "")
|
FilesystemNode sym(myOSystem.romFile().getPathWithExt(".sym"));
|
||||||
{
|
if(!sym.isReadable())
|
||||||
FilesystemNode sym(myOSystem.romFile().getPathWithExt("") + ".sym");
|
|
||||||
if(sym.isFile() && sym.isReadable())
|
|
||||||
mySymbolFile = sym.getPath();
|
|
||||||
else
|
|
||||||
return DebuggerParser::red("symbol file \'" + sym.getShortPath() + "\' not found");
|
return DebuggerParser::red("symbol file \'" + sym.getShortPath() + "\' not found");
|
||||||
}
|
|
||||||
|
|
||||||
FilesystemNode node(mySymbolFile);
|
|
||||||
ifstream in(node.getPath());
|
|
||||||
if(!in.is_open())
|
|
||||||
return DebuggerParser::red("symbol file '" + node.getShortPath() + "' not readable");
|
|
||||||
|
|
||||||
myUserAddresses.clear();
|
myUserAddresses.clear();
|
||||||
myUserLabels.clear();
|
myUserLabels.clear();
|
||||||
|
|
||||||
|
stringstream in;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(sym.read(in) == 0)
|
||||||
|
return DebuggerParser::red("symbol file '" + sym.getShortPath() + "' not found");
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return DebuggerParser::red("symbol file '" + sym.getShortPath() + "' not readable");
|
||||||
|
}
|
||||||
|
|
||||||
while(!in.eof())
|
while(!in.eof())
|
||||||
{
|
{
|
||||||
string label;
|
string label;
|
||||||
|
@ -807,28 +852,29 @@ string CartDebug::loadSymbolFile()
|
||||||
}
|
}
|
||||||
myDebugger.rom().invalidate();
|
myDebugger.rom().invalidate();
|
||||||
|
|
||||||
return "symbol file '" + node.getShortPath() + "' loaded OK";
|
return "symbol file '" + sym.getShortPath() + "' loaded OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string CartDebug::loadConfigFile()
|
string CartDebug::loadConfigFile()
|
||||||
{
|
{
|
||||||
// The default naming/location for config files is the ROM dir based on the
|
// The default naming/location for config files is the CFG dir and based
|
||||||
// actual ROM filename
|
// on the actual ROM filename
|
||||||
|
|
||||||
if(myCfgFile == "")
|
FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg"));
|
||||||
{
|
FilesystemNode cfg = myOSystem.cfgDir(); cfg /= romNode.getName();
|
||||||
FilesystemNode cfg(myOSystem.romFile().getPathWithExt("") + ".cfg");
|
if(!cfg.isReadable())
|
||||||
if(cfg.isFile() && cfg.isReadable())
|
|
||||||
myCfgFile = cfg.getPath();
|
|
||||||
else
|
|
||||||
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not found");
|
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not found");
|
||||||
}
|
|
||||||
|
|
||||||
FilesystemNode node(myCfgFile);
|
stringstream in;
|
||||||
ifstream in(node.getPath());
|
try
|
||||||
if(!in.is_open())
|
{
|
||||||
return "Unable to load directives from " + node.getPath();
|
cfg.read(in);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return "Unable to load directives from " + cfg.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
// Erase all previous directives
|
// Erase all previous directives
|
||||||
for(auto& bi: myBankInfo)
|
for(auto& bi: myBankInfo)
|
||||||
|
@ -876,37 +922,57 @@ string CartDebug::loadConfigFile()
|
||||||
else if(BSPF::startsWithIgnoreCase(directive, "CODE"))
|
else if(BSPF::startsWithIgnoreCase(directive, "CODE"))
|
||||||
{
|
{
|
||||||
buf >> hex >> start >> hex >> end;
|
buf >> hex >> start >> hex >> end;
|
||||||
addDirective(CartDebug::CODE, start, end, currentbank);
|
addDirective(Device::CODE, start, end, currentbank);
|
||||||
}
|
}
|
||||||
else if(BSPF::startsWithIgnoreCase(directive, "GFX"))
|
else if(BSPF::startsWithIgnoreCase(directive, "GFX"))
|
||||||
{
|
{
|
||||||
buf >> hex >> start >> hex >> end;
|
buf >> hex >> start >> hex >> end;
|
||||||
addDirective(CartDebug::GFX, start, end, currentbank);
|
addDirective(Device::GFX, start, end, currentbank);
|
||||||
}
|
}
|
||||||
else if(BSPF::startsWithIgnoreCase(directive, "PGFX"))
|
else if(BSPF::startsWithIgnoreCase(directive, "PGFX"))
|
||||||
{
|
{
|
||||||
buf >> hex >> start >> hex >> end;
|
buf >> hex >> start >> hex >> end;
|
||||||
addDirective(CartDebug::PGFX, start, end, currentbank);
|
addDirective(Device::PGFX, start, end, currentbank);
|
||||||
|
}
|
||||||
|
else if(BSPF::startsWithIgnoreCase(directive, "COL"))
|
||||||
|
{
|
||||||
|
buf >> hex >> start >> hex >> end;
|
||||||
|
addDirective(Device::COL, start, end, currentbank);
|
||||||
|
}
|
||||||
|
else if(BSPF::startsWithIgnoreCase(directive, "PCOL"))
|
||||||
|
{
|
||||||
|
buf >> hex >> start >> hex >> end;
|
||||||
|
addDirective(Device::PCOL, start, end, currentbank);
|
||||||
|
}
|
||||||
|
else if(BSPF::startsWithIgnoreCase(directive, "BCOL"))
|
||||||
|
{
|
||||||
|
buf >> hex >> start >> hex >> end;
|
||||||
|
addDirective(Device::BCOL, start, end, currentbank);
|
||||||
|
}
|
||||||
|
else if(BSPF::startsWithIgnoreCase(directive, "AUD"))
|
||||||
|
{
|
||||||
|
buf >> hex >> start >> hex >> end;
|
||||||
|
addDirective(Device::AUD, start, end, currentbank);
|
||||||
}
|
}
|
||||||
else if(BSPF::startsWithIgnoreCase(directive, "DATA"))
|
else if(BSPF::startsWithIgnoreCase(directive, "DATA"))
|
||||||
{
|
{
|
||||||
buf >> hex >> start >> hex >> end;
|
buf >> hex >> start >> hex >> end;
|
||||||
addDirective(CartDebug::DATA, start, end, currentbank);
|
addDirective(Device::DATA, start, end, currentbank);
|
||||||
}
|
}
|
||||||
else if(BSPF::startsWithIgnoreCase(directive, "ROW"))
|
else if(BSPF::startsWithIgnoreCase(directive, "ROW"))
|
||||||
{
|
{
|
||||||
buf >> hex >> start;
|
buf >> hex >> start;
|
||||||
buf >> hex >> end;
|
buf >> hex >> end;
|
||||||
addDirective(CartDebug::ROW, start, end, currentbank);
|
addDirective(Device::ROW, start, end, currentbank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myDebugger.rom().invalidate();
|
myDebugger.rom().invalidate();
|
||||||
|
|
||||||
stringstream retVal;
|
stringstream retVal;
|
||||||
if(myConsole.cartridge().bankCount() > 1)
|
if(myConsole.cartridge().romBankCount() > 1)
|
||||||
retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n");
|
retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n");
|
||||||
retVal << "config file '" << node.getShortPath() << "' loaded OK";
|
retVal << "config file '" << cfg.getShortPath() << "' loaded OK";
|
||||||
return retVal.str();
|
return retVal.str();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -914,69 +980,72 @@ string CartDebug::loadConfigFile()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string CartDebug::saveConfigFile()
|
string CartDebug::saveConfigFile()
|
||||||
{
|
{
|
||||||
// The default naming/location for config files is the ROM dir based on the
|
// The default naming/location for config files is the CFG dir and based
|
||||||
// actual ROM filename
|
// on the actual ROM filename
|
||||||
|
|
||||||
FilesystemNode cfg;
|
|
||||||
if(myCfgFile == "")
|
|
||||||
{
|
|
||||||
cfg = FilesystemNode(myOSystem.romFile().getPathWithExt("") + ".cfg");
|
|
||||||
if(cfg.isFile() && cfg.isWritable())
|
|
||||||
myCfgFile = cfg.getPath();
|
|
||||||
else
|
|
||||||
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not writable");
|
|
||||||
}
|
|
||||||
|
|
||||||
const string& name = myConsole.properties().get(PropType::Cart_Name);
|
const string& name = myConsole.properties().get(PropType::Cart_Name);
|
||||||
const string& md5 = myConsole.properties().get(PropType::Cart_MD5);
|
const string& md5 = myConsole.properties().get(PropType::Cart_MD5);
|
||||||
|
|
||||||
ofstream out(cfg.getPath());
|
|
||||||
if(!out.is_open())
|
|
||||||
return "Unable to save directives to " + cfg.getShortPath();
|
|
||||||
|
|
||||||
// Store all bank information
|
// Store all bank information
|
||||||
|
stringstream out;
|
||||||
out << "// Stella.pro: \"" << name << "\"" << endl
|
out << "// Stella.pro: \"" << name << "\"" << endl
|
||||||
<< "// MD5: " << md5 << endl
|
<< "// MD5: " << md5 << endl
|
||||||
<< endl;
|
<< endl;
|
||||||
for(uInt32 b = 0; b < myConsole.cartridge().bankCount(); ++b)
|
for(uInt32 b = 0; b < myConsole.cartridge().romBankCount(); ++b)
|
||||||
{
|
{
|
||||||
out << "[" << b << "]" << endl;
|
out << "[" << b << "]" << endl;
|
||||||
getBankDirectives(out, myBankInfo[b]);
|
getBankDirectives(out, myBankInfo[b]);
|
||||||
}
|
}
|
||||||
|
|
||||||
stringstream retVal;
|
stringstream retVal;
|
||||||
if(myConsole.cartridge().bankCount() > 1)
|
try
|
||||||
|
{
|
||||||
|
FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg"));
|
||||||
|
FilesystemNode cfg = myOSystem.cfgDir(); cfg /= romNode.getName();
|
||||||
|
if(!cfg.getParent().isWritable())
|
||||||
|
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not writable");
|
||||||
|
|
||||||
|
if(cfg.write(out) == 0)
|
||||||
|
return "Unable to save directives to " + cfg.getShortPath();
|
||||||
|
|
||||||
|
if(myConsole.cartridge().romBankCount() > 1)
|
||||||
retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n");
|
retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n");
|
||||||
retVal << "config file '" << cfg.getShortPath() << "' saved OK";
|
retVal << "config file '" << cfg.getShortPath() << "' saved OK";
|
||||||
|
}
|
||||||
|
catch(const runtime_error& e)
|
||||||
|
{
|
||||||
|
retVal << "Unable to save directives: " << e.what();
|
||||||
|
}
|
||||||
return retVal.str();
|
return retVal.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string CartDebug::saveDisassembly()
|
string CartDebug::saveDisassembly()
|
||||||
{
|
{
|
||||||
if(myDisasmFile == "")
|
string NTSC_COLOR[16] = {
|
||||||
{
|
"BLACK", "YELLOW", "BROWN", "ORANGE",
|
||||||
const string& propsname =
|
"RED", "MAUVE", "VIOLET", "PURPLE",
|
||||||
myConsole.properties().get(PropType::Cart_Name) + ".asm";
|
"BLUE", "BLUE_CYAN", "CYAN", "CYAN_GREEN",
|
||||||
|
"GREEN", "GREEN_YELLOW", "GREEN_BEIGE", "BEIGE"
|
||||||
myDisasmFile = FilesystemNode(myOSystem.defaultSaveDir() + propsname).getPath();
|
};
|
||||||
}
|
string PAL_COLOR[16] = {
|
||||||
|
"BLACK0", "BLACK1", "YELLOW", "GREEN_YELLOW",
|
||||||
FilesystemNode node(myDisasmFile);
|
"ORANGE", "GREEN", "RED", "CYAN_GREEN",
|
||||||
ofstream out(node.getPath());
|
"MAUVE", "CYAN", "VIOLET", "BLUE_CYAN",
|
||||||
if(!out.is_open())
|
"PURPLE", "BLUE", "BLACKE", "BLACKF"
|
||||||
return "Unable to save disassembly to " + node.getShortPath();
|
};
|
||||||
|
string SECAM_COLOR[8] = {
|
||||||
|
"BLACK", "BLUE", "RED", "PURPLE",
|
||||||
|
"GREEN", "CYAN", "YELLOW", "WHITE"
|
||||||
|
};
|
||||||
|
bool isNTSC = myConsole.timing() == ConsoleTiming::ntsc;
|
||||||
|
bool isPAL = myConsole.timing() == ConsoleTiming::pal;
|
||||||
|
|
||||||
#define ALIGN(x) setfill(' ') << left << setw(x)
|
#define ALIGN(x) setfill(' ') << left << setw(x)
|
||||||
|
|
||||||
// We can't print the header to the disassembly until it's actually
|
// We can't print the header to the disassembly until it's actually
|
||||||
// been processed; therefore buffer output to a string first
|
// been processed; therefore buffer output to a string first
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
buf << "\n\n;***********************************************************\n"
|
|
||||||
<< "; Bank " << myConsole.cartridge().getBank();
|
|
||||||
if (myConsole.cartridge().bankCount() > 1)
|
|
||||||
buf << " / 0.." << myConsole.cartridge().bankCount() - 1;
|
|
||||||
buf << "\n;***********************************************************\n\n";
|
|
||||||
|
|
||||||
// Use specific settings for disassembly output
|
// Use specific settings for disassembly output
|
||||||
// This will most likely differ from what you see in the debugger
|
// This will most likely differ from what you see in the debugger
|
||||||
|
@ -992,13 +1061,34 @@ string CartDebug::saveDisassembly()
|
||||||
|
|
||||||
Disassembly disasm;
|
Disassembly disasm;
|
||||||
disasm.list.reserve(2048);
|
disasm.list.reserve(2048);
|
||||||
for(int bank = 0; bank < myConsole.cartridge().bankCount(); ++bank)
|
uInt16 romBankCount = myConsole.cartridge().romBankCount();
|
||||||
|
uInt16 oldBank = myConsole.cartridge().getBank();
|
||||||
|
|
||||||
|
// prepare for switching banks
|
||||||
|
myConsole.cartridge().unlockBank();
|
||||||
|
uInt32 origin = 0;
|
||||||
|
|
||||||
|
for(int bank = 0; bank < romBankCount; ++bank)
|
||||||
{
|
{
|
||||||
|
// TODO: not every CartDebugWidget does it like that, we need a method
|
||||||
|
myConsole.cartridge().unlockBank();
|
||||||
|
myConsole.cartridge().bank(bank);
|
||||||
|
myConsole.cartridge().lockBank();
|
||||||
|
|
||||||
BankInfo& info = myBankInfo[bank];
|
BankInfo& info = myBankInfo[bank];
|
||||||
|
|
||||||
|
disassembleBank(bank);
|
||||||
|
|
||||||
// An empty address list means that DiStella can't do a disassembly
|
// An empty address list means that DiStella can't do a disassembly
|
||||||
if(info.addressList.size() == 0)
|
if(info.addressList.size() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
buf << "\n\n;***********************************************************\n"
|
||||||
|
<< "; Bank " << bank;
|
||||||
|
if (romBankCount > 1)
|
||||||
|
buf << " / 0.." << romBankCount - 1;
|
||||||
|
buf << "\n;***********************************************************\n\n";
|
||||||
|
|
||||||
// Disassemble bank
|
// Disassemble bank
|
||||||
disasm.list.clear();
|
disasm.list.clear();
|
||||||
DiStella distella(*this, disasm.list, info, settings,
|
DiStella distella(*this, disasm.list, info, settings,
|
||||||
|
@ -1007,8 +1097,14 @@ string CartDebug::saveDisassembly()
|
||||||
if (myReserved.breakFound)
|
if (myReserved.breakFound)
|
||||||
addLabel("Break", myDebugger.dpeek(0xfffe));
|
addLabel("Break", myDebugger.dpeek(0xfffe));
|
||||||
|
|
||||||
buf << " SEG CODE\n"
|
buf << " SEG CODE\n";
|
||||||
<< " ORG $" << Base::HEX4 << info.offset << "\n\n";
|
|
||||||
|
if(romBankCount == 1)
|
||||||
|
buf << " ORG $" << Base::HEX4 << info.offset << "\n\n";
|
||||||
|
else
|
||||||
|
buf << " ORG $" << Base::HEX4 << origin << "\n"
|
||||||
|
<< " RORG $" << Base::HEX4 << info.offset << "\n\n";
|
||||||
|
origin += uInt32(info.size);
|
||||||
|
|
||||||
// Format in 'distella' style
|
// Format in 'distella' style
|
||||||
for(uInt32 i = 0; i < disasm.list.size(); ++i)
|
for(uInt32 i = 0; i < disasm.list.size(); ++i)
|
||||||
|
@ -1022,53 +1118,66 @@ string CartDebug::saveDisassembly()
|
||||||
|
|
||||||
switch(tag.type)
|
switch(tag.type)
|
||||||
{
|
{
|
||||||
case CartDebug::CODE:
|
case Device::CODE:
|
||||||
{
|
|
||||||
buf << ALIGN(32) << tag.disasm << tag.ccount.substr(0, 5) << tag.ctotal << tag.ccount.substr(5, 2);
|
buf << ALIGN(32) << tag.disasm << tag.ccount.substr(0, 5) << tag.ctotal << tag.ccount.substr(5, 2);
|
||||||
if (tag.disasm.find("WSYNC") != std::string::npos)
|
if (tag.disasm.find("WSYNC") != std::string::npos)
|
||||||
buf << "\n;---------------------------------------";
|
buf << "\n;---------------------------------------";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case CartDebug::ROW:
|
case Device::ROW:
|
||||||
{
|
|
||||||
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8*4-1) << "; $" << Base::HEX4 << tag.address << " (*)";
|
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8*4-1) << "; $" << Base::HEX4 << tag.address << " (*)";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case CartDebug::GFX:
|
case Device::GFX:
|
||||||
{
|
|
||||||
buf << ".byte " << (settings.gfxFormat == Base::Fmt::_2 ? "%" : "$")
|
buf << ".byte " << (settings.gfxFormat == Base::Fmt::_2 ? "%" : "$")
|
||||||
<< tag.bytes << " ; |";
|
<< tag.bytes << " ; |";
|
||||||
for(int c = 12; c < 20; ++c)
|
for(int c = 12; c < 20; ++c)
|
||||||
buf << ((tag.disasm[c] == '\x1e') ? "#" : " ");
|
buf << ((tag.disasm[c] == '\x1e') ? "#" : " ");
|
||||||
buf << ALIGN(13) << "|" << "$" << Base::HEX4 << tag.address << " (G)";
|
buf << ALIGN(13) << "|" << "$" << Base::HEX4 << tag.address << " (G)";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case CartDebug::PGFX:
|
case Device::PGFX:
|
||||||
{
|
|
||||||
buf << ".byte " << (settings.gfxFormat == Base::Fmt::_2 ? "%" : "$")
|
buf << ".byte " << (settings.gfxFormat == Base::Fmt::_2 ? "%" : "$")
|
||||||
<< tag.bytes << " ; |";
|
<< tag.bytes << " ; |";
|
||||||
for(int c = 12; c < 20; ++c)
|
for(int c = 12; c < 20; ++c)
|
||||||
buf << ((tag.disasm[c] == '\x1f') ? "*" : " ");
|
buf << ((tag.disasm[c] == '\x1f') ? "*" : " ");
|
||||||
buf << ALIGN(13) << "|" << "$" << Base::HEX4 << tag.address << " (P)";
|
buf << ALIGN(13) << "|" << "$" << Base::HEX4 << tag.address << " (P)";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case CartDebug::DATA:
|
case Device::COL:
|
||||||
{
|
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 15) << "; $" << Base::HEX4 << tag.address << " (C)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Device::PCOL:
|
||||||
|
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 15) << "; $" << Base::HEX4 << tag.address << " (CP)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Device::BCOL:
|
||||||
|
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 15) << "; $" << Base::HEX4 << tag.address << " (CB)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Device::AUD:
|
||||||
|
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8 * 4 - 1) << "; $" << Base::HEX4 << tag.address << " (A)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Device::DATA:
|
||||||
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8 * 4 - 1) << "; $" << Base::HEX4 << tag.address << " (D)";
|
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8 * 4 - 1) << "; $" << Base::HEX4 << tag.address << " (D)";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case CartDebug::NONE:
|
case Device::NONE:
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
} // switch
|
} // switch
|
||||||
buf << "\n";
|
buf << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
myConsole.cartridge().unlockBank();
|
||||||
|
myConsole.cartridge().bank(oldBank);
|
||||||
|
myConsole.cartridge().lockBank();
|
||||||
|
|
||||||
// Some boilerplate, similar to what DiStella adds
|
// Some boilerplate, similar to what DiStella adds
|
||||||
auto timeinfo = BSPF::localTime();
|
auto timeinfo = BSPF::localTime();
|
||||||
|
stringstream out;
|
||||||
out << "; Disassembly of " << myOSystem.romFile().getShortPath() << "\n"
|
out << "; Disassembly of " << myOSystem.romFile().getShortPath() << "\n"
|
||||||
<< "; Disassembled " << std::put_time(&timeinfo, "%c\n")
|
<< "; Disassembled " << std::put_time(&timeinfo, "%c\n")
|
||||||
<< "; Using Stella " << STELLA_VERSION << "\n;\n"
|
<< "; Using Stella " << STELLA_VERSION << "\n;\n"
|
||||||
|
@ -1079,19 +1188,45 @@ string CartDebug::saveDisassembly()
|
||||||
<< "; D = DATA directive (referenced in some way)\n"
|
<< "; D = DATA directive (referenced in some way)\n"
|
||||||
<< "; G = GFX directive, shown as '#' (stored in player, missile, ball)\n"
|
<< "; G = GFX directive, shown as '#' (stored in player, missile, ball)\n"
|
||||||
<< "; P = PGFX directive, shown as '*' (stored in playfield)\n"
|
<< "; P = PGFX directive, shown as '*' (stored in playfield)\n"
|
||||||
|
<< "; C = COL directive, shown as color constants (stored in player color)\n"
|
||||||
|
<< "; CP = PCOL directive, shown as color constants (stored in playfield color)\n"
|
||||||
|
<< "; CB = BCOL directive, shown as color constants (stored in background color)\n"
|
||||||
|
<< "; A = AUD directive (stored in audio registers)\n"
|
||||||
<< "; i = indexed accessed only\n"
|
<< "; i = indexed accessed only\n"
|
||||||
<< "; c = used by code executed in RAM\n"
|
<< "; c = used by code executed in RAM\n"
|
||||||
<< "; s = used by stack\n"
|
<< "; s = used by stack\n"
|
||||||
<< "; ! = page crossed, 1 cycle penalty\n"
|
<< "; ! = page crossed, 1 cycle penalty\n"
|
||||||
<< "\n processor 6502\n\n";
|
<< "\n processor 6502\n\n";
|
||||||
|
|
||||||
|
out << "\n;-----------------------------------------------------------\n"
|
||||||
|
<< "; Color constants\n"
|
||||||
|
<< ";-----------------------------------------------------------\n\n";
|
||||||
|
|
||||||
|
if(isNTSC)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 16; ++i)
|
||||||
|
out << ALIGN(16) << NTSC_COLOR[i] << " = $" << Base::HEX2 << (i << 4) << "\n";
|
||||||
|
}
|
||||||
|
else if(isPAL)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 16; ++i)
|
||||||
|
out << ALIGN(16) << PAL_COLOR[i] << " = $" << Base::HEX2 << (i << 4) << "\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 8; ++i)
|
||||||
|
out << ALIGN(16) << SECAM_COLOR[i] << " = $" << Base::HEX1 << (i << 1) << "\n";
|
||||||
|
}
|
||||||
|
out << "\n";
|
||||||
|
|
||||||
bool addrUsed = false;
|
bool addrUsed = false;
|
||||||
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
|
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
|
||||||
addrUsed = addrUsed || myReserved.TIARead[addr] || (mySystem.getAccessFlags(addr) & WRITE);
|
addrUsed = addrUsed || myReserved.TIARead[addr] || (mySystem.getAccessFlags(addr) & Device::WRITE);
|
||||||
for(uInt16 addr = 0x00; addr <= 0x3F; ++addr)
|
for(uInt16 addr = 0x00; addr <= 0x3F; ++addr)
|
||||||
addrUsed = addrUsed || myReserved.TIAWrite[addr] || (mySystem.getAccessFlags(addr) & DATA);
|
addrUsed = addrUsed || myReserved.TIAWrite[addr] || (mySystem.getAccessFlags(addr) & Device::DATA);
|
||||||
for(uInt16 addr = 0x00; addr <= 0x17; ++addr)
|
for(uInt16 addr = 0x00; addr <= 0x17; ++addr)
|
||||||
addrUsed = addrUsed || myReserved.IOReadWrite[addr];
|
addrUsed = addrUsed || myReserved.IOReadWrite[addr];
|
||||||
|
|
||||||
if(addrUsed)
|
if(addrUsed)
|
||||||
{
|
{
|
||||||
out << "\n;-----------------------------------------------------------\n"
|
out << "\n;-----------------------------------------------------------\n"
|
||||||
|
@ -1103,7 +1238,7 @@ string CartDebug::saveDisassembly()
|
||||||
if(myReserved.TIARead[addr] && ourTIAMnemonicR[addr])
|
if(myReserved.TIARead[addr] && ourTIAMnemonicR[addr])
|
||||||
out << ALIGN(16) << ourTIAMnemonicR[addr] << "= $"
|
out << ALIGN(16) << ourTIAMnemonicR[addr] << "= $"
|
||||||
<< Base::HEX2 << right << addr << " ; (R)\n";
|
<< Base::HEX2 << right << addr << " ; (R)\n";
|
||||||
else if (mySystem.getAccessFlags(addr) & DATA)
|
else if (mySystem.getAccessFlags(addr) & Device::DATA)
|
||||||
out << ";" << ALIGN(16-1) << ourTIAMnemonicR[addr] << "= $"
|
out << ";" << ALIGN(16-1) << ourTIAMnemonicR[addr] << "= $"
|
||||||
<< Base::HEX2 << right << addr << " ; (Ri)\n";
|
<< Base::HEX2 << right << addr << " ; (Ri)\n";
|
||||||
out << "\n";
|
out << "\n";
|
||||||
|
@ -1113,7 +1248,7 @@ string CartDebug::saveDisassembly()
|
||||||
if(myReserved.TIAWrite[addr] && ourTIAMnemonicW[addr])
|
if(myReserved.TIAWrite[addr] && ourTIAMnemonicW[addr])
|
||||||
out << ALIGN(16) << ourTIAMnemonicW[addr] << "= $"
|
out << ALIGN(16) << ourTIAMnemonicW[addr] << "= $"
|
||||||
<< Base::HEX2 << right << addr << " ; (W)\n";
|
<< Base::HEX2 << right << addr << " ; (W)\n";
|
||||||
else if (mySystem.getAccessFlags(addr) & WRITE)
|
else if (mySystem.getAccessFlags(addr) & Device::WRITE)
|
||||||
out << ";" << ALIGN(16-1) << ourTIAMnemonicW[addr] << "= $"
|
out << ";" << ALIGN(16-1) << ourTIAMnemonicW[addr] << "= $"
|
||||||
<< Base::HEX2 << right << addr << " ; (Wi)\n";
|
<< Base::HEX2 << right << addr << " ; (Wi)\n";
|
||||||
out << "\n";
|
out << "\n";
|
||||||
|
@ -1128,8 +1263,8 @@ string CartDebug::saveDisassembly()
|
||||||
addrUsed = false;
|
addrUsed = false;
|
||||||
for(uInt16 addr = 0x80; addr <= 0xFF; ++addr)
|
for(uInt16 addr = 0x80; addr <= 0xFF; ++addr)
|
||||||
addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80]
|
addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80]
|
||||||
|| (mySystem.getAccessFlags(addr) & (DATA | WRITE))
|
|| (mySystem.getAccessFlags(addr) & (Device::DATA | Device::WRITE))
|
||||||
|| (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
|
|| (mySystem.getAccessFlags(addr|0x100) & (Device::DATA | Device::WRITE));
|
||||||
if(addrUsed)
|
if(addrUsed)
|
||||||
{
|
{
|
||||||
bool addLine = false;
|
bool addLine = false;
|
||||||
|
@ -1138,9 +1273,9 @@ string CartDebug::saveDisassembly()
|
||||||
<< ";-----------------------------------------------------------\n\n";
|
<< ";-----------------------------------------------------------\n\n";
|
||||||
|
|
||||||
for (uInt16 addr = 0x80; addr <= 0xFF; ++addr) {
|
for (uInt16 addr = 0x80; addr <= 0xFF; ++addr) {
|
||||||
bool ramUsed = (mySystem.getAccessFlags(addr) & (DATA | WRITE));
|
bool ramUsed = (mySystem.getAccessFlags(addr) & (Device::DATA | Device::WRITE));
|
||||||
bool codeUsed = (mySystem.getAccessFlags(addr) & CODE);
|
bool codeUsed = (mySystem.getAccessFlags(addr) & Device::CODE);
|
||||||
bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
|
bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (Device::DATA | Device::WRITE));
|
||||||
|
|
||||||
if (myReserved.ZPRAM[addr - 0x80] &&
|
if (myReserved.ZPRAM[addr - 0x80] &&
|
||||||
myUserLabels.find(addr) == myUserLabels.end()) {
|
myUserLabels.find(addr) == myUserLabels.end()) {
|
||||||
|
@ -1194,10 +1329,22 @@ string CartDebug::saveDisassembly()
|
||||||
// And finally, output the disassembly
|
// And finally, output the disassembly
|
||||||
out << buf.str();
|
out << buf.str();
|
||||||
|
|
||||||
|
const string& propsname =
|
||||||
|
myConsole.properties().get(PropType::Cart_Name) + ".asm";
|
||||||
|
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + propsname);
|
||||||
stringstream retVal;
|
stringstream retVal;
|
||||||
if(myConsole.cartridge().bankCount() > 1)
|
try
|
||||||
retVal << DebuggerParser::red("disassembly for multi-bank ROM not fully supported, only currently enabled banks disassembled\n");
|
{
|
||||||
|
node.write(out);
|
||||||
|
|
||||||
|
if(myConsole.cartridge().romBankCount() > 1)
|
||||||
|
retVal << DebuggerParser::red("disassembly for multi-bank ROM not fully supported\n");
|
||||||
retVal << "saved " << node.getShortPath() << " OK";
|
retVal << "saved " << node.getShortPath() << " OK";
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
retVal << "Unable to save disassembly to " << node.getShortPath();
|
||||||
|
}
|
||||||
return retVal.str();
|
return retVal.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,22 +1353,40 @@ string CartDebug::saveRom()
|
||||||
{
|
{
|
||||||
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26";
|
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26";
|
||||||
|
|
||||||
FilesystemNode node(myOSystem.defaultSaveDir() + rom);
|
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
|
||||||
ofstream out(node.getPath(), std::ios::binary);
|
if(myConsole.cartridge().saveROM(node))
|
||||||
if(out && myConsole.cartridge().saveROM(out))
|
|
||||||
return "saved ROM as " + node.getShortPath();
|
return "saved ROM as " + node.getShortPath();
|
||||||
else
|
else
|
||||||
return DebuggerParser::red("failed to save ROM");
|
return DebuggerParser::red("failed to save ROM");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
string CartDebug::saveAccessFile()
|
||||||
|
{
|
||||||
|
stringstream out;
|
||||||
|
out << myConsole.tia().getAccessCounters();
|
||||||
|
out << myConsole.riot().getAccessCounters();
|
||||||
|
out << myConsole.cartridge().getAccessCounters();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".csv";
|
||||||
|
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
|
||||||
|
|
||||||
|
node.write(out);
|
||||||
|
return "saved access counters as " + node.getShortPath();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return DebuggerParser::red("failed to save access counters file");
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string CartDebug::listConfig(int bank)
|
string CartDebug::listConfig(int bank)
|
||||||
{
|
{
|
||||||
if(myConsole.cartridge().bankCount() > 1)
|
uInt32 startbank = 0, endbank = romBankCount();
|
||||||
return DebuggerParser::red("config file for multi-bank ROM not yet supported");
|
if(bank >= 0 && bank < romBankCount())
|
||||||
|
|
||||||
uInt32 startbank = 0, endbank = bankCount();
|
|
||||||
if(bank >= 0 && bank < bankCount())
|
|
||||||
{
|
{
|
||||||
startbank = bank;
|
startbank = bank;
|
||||||
endbank = startbank + 1;
|
endbank = startbank + 1;
|
||||||
|
@ -1232,27 +1397,30 @@ string CartDebug::listConfig(int bank)
|
||||||
for(uInt32 b = startbank; b < endbank; ++b)
|
for(uInt32 b = startbank; b < endbank; ++b)
|
||||||
{
|
{
|
||||||
BankInfo& info = myBankInfo[b];
|
BankInfo& info = myBankInfo[b];
|
||||||
buf << "[" << b << "]" << endl;
|
buf << "Bank [" << b << "]" << endl;
|
||||||
for(const auto& i: info.directiveList)
|
for(const auto& i: info.directiveList)
|
||||||
{
|
{
|
||||||
if(i.type != CartDebug::NONE)
|
if(i.type != Device::NONE)
|
||||||
{
|
{
|
||||||
buf << "(*) ";
|
buf << "(*) ";
|
||||||
disasmTypeAsString(buf, i.type);
|
AccessTypeAsString(buf, i.type);
|
||||||
buf << " " << Base::HEX4 << i.start << " " << Base::HEX4 << i.end << endl;
|
buf << " " << Base::HEX4 << i.start << " " << Base::HEX4 << i.end << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getBankDirectives(buf, info);
|
getBankDirectives(buf, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(myConsole.cartridge().romBankCount() > 1)
|
||||||
|
buf << DebuggerParser::red("config file for multi-bank ROM not fully supported") << endl;
|
||||||
|
|
||||||
return buf.str();
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string CartDebug::clearConfig(int bank)
|
string CartDebug::clearConfig(int bank)
|
||||||
{
|
{
|
||||||
uInt32 startbank = 0, endbank = bankCount();
|
uInt32 startbank = 0, endbank = romBankCount();
|
||||||
if(bank >= 0 && bank < bankCount())
|
if(bank >= 0 && bank < romBankCount())
|
||||||
{
|
{
|
||||||
startbank = bank;
|
startbank = bank;
|
||||||
endbank = startbank + 1;
|
endbank = startbank + 1;
|
||||||
|
@ -1334,15 +1502,15 @@ void CartDebug::getBankDirectives(ostream& buf, BankInfo& info) const
|
||||||
|
|
||||||
// Now consider each byte
|
// Now consider each byte
|
||||||
uInt32 prev = info.offset, addr = prev + 1;
|
uInt32 prev = info.offset, addr = prev + 1;
|
||||||
DisasmType prevType = disasmTypeAbsolute(mySystem.getAccessFlags(prev));
|
Device::AccessType prevType = accessTypeAbsolute(mySystem.getAccessFlags(prev));
|
||||||
for( ; addr < info.offset + info.size; ++addr)
|
for( ; addr < info.offset + info.size; ++addr)
|
||||||
{
|
{
|
||||||
DisasmType currType = disasmTypeAbsolute(mySystem.getAccessFlags(addr));
|
Device::AccessType currType = accessTypeAbsolute(mySystem.getAccessFlags(addr));
|
||||||
|
|
||||||
// Have we changed to a new type?
|
// Have we changed to a new type?
|
||||||
if(currType != prevType)
|
if(currType != prevType)
|
||||||
{
|
{
|
||||||
disasmTypeAsString(buf, prevType);
|
AccessTypeAsString(buf, prevType);
|
||||||
buf << " " << Base::HEX4 << prev << " " << Base::HEX4 << (addr-1) << endl;
|
buf << " " << Base::HEX4 << prev << " " << Base::HEX4 << (addr-1) << endl;
|
||||||
|
|
||||||
prev = addr;
|
prev = addr;
|
||||||
|
@ -1353,13 +1521,13 @@ void CartDebug::getBankDirectives(ostream& buf, BankInfo& info) const
|
||||||
// Grab the last directive, making sure it accounts for all remaining space
|
// Grab the last directive, making sure it accounts for all remaining space
|
||||||
if(prev != addr)
|
if(prev != addr)
|
||||||
{
|
{
|
||||||
disasmTypeAsString(buf, prevType);
|
AccessTypeAsString(buf, prevType);
|
||||||
buf << " " << Base::HEX4 << prev << " " << Base::HEX4 << (addr-1) << endl;
|
buf << " " << Base::HEX4 << prev << " " << Base::HEX4 << (addr-1) << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void CartDebug::addressTypeAsString(ostream& buf, uInt16 addr) const
|
void CartDebug::accessTypeAsString(ostream& buf, uInt16 addr) const
|
||||||
{
|
{
|
||||||
if(!(addr & 0x1000))
|
if(!(addr & 0x1000))
|
||||||
{
|
{
|
||||||
|
@ -1372,70 +1540,88 @@ void CartDebug::addressTypeAsString(ostream& buf, uInt16 addr) const
|
||||||
label = myDisLabels[addr & 0xFFF];
|
label = myDisLabels[addr & 0xFFF];
|
||||||
|
|
||||||
buf << endl << "directive: " << Base::toString(directive, Base::Fmt::_2_8) << " ";
|
buf << endl << "directive: " << Base::toString(directive, Base::Fmt::_2_8) << " ";
|
||||||
disasmTypeAsString(buf, directive);
|
AccessTypeAsString(buf, directive);
|
||||||
buf << endl << "emulation: " << Base::toString(debugger, Base::Fmt::_2_8) << " ";
|
buf << endl << "emulation: " << Base::toString(debugger, Base::Fmt::_2_8) << " ";
|
||||||
disasmTypeAsString(buf, debugger);
|
AccessTypeAsString(buf, debugger);
|
||||||
buf << endl << "tentative: " << Base::toString(label, Base::Fmt::_2_8) << " ";
|
buf << endl << "tentative: " << Base::toString(label, Base::Fmt::_2_8) << " ";
|
||||||
disasmTypeAsString(buf, label);
|
AccessTypeAsString(buf, label);
|
||||||
buf << endl;
|
buf << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
CartDebug::DisasmType CartDebug::disasmTypeAbsolute(uInt8 flags) const
|
Device::AccessType CartDebug::accessTypeAbsolute(Device::AccessFlags flags) const
|
||||||
{
|
{
|
||||||
if(flags & CartDebug::CODE)
|
if(flags & Device::CODE)
|
||||||
return CartDebug::CODE;
|
return Device::CODE;
|
||||||
else if(flags & CartDebug::TCODE)
|
else if(flags & Device::TCODE)
|
||||||
return CartDebug::CODE; // TODO - should this be separate??
|
return Device::CODE; // TODO - should this be separate??
|
||||||
else if(flags & CartDebug::GFX)
|
else if(flags & Device::GFX)
|
||||||
return CartDebug::GFX;
|
return Device::GFX;
|
||||||
else if(flags & CartDebug::PGFX)
|
else if(flags & Device::PGFX)
|
||||||
return CartDebug::PGFX;
|
return Device::PGFX;
|
||||||
else if(flags & CartDebug::DATA)
|
else if(flags & Device::COL)
|
||||||
return CartDebug::DATA;
|
return Device::COL;
|
||||||
else if(flags & CartDebug::ROW)
|
else if(flags & Device::PCOL)
|
||||||
return CartDebug::ROW;
|
return Device::PCOL;
|
||||||
|
else if(flags & Device::BCOL)
|
||||||
|
return Device::BCOL;
|
||||||
|
else if(flags & Device::AUD)
|
||||||
|
return Device::AUD;
|
||||||
|
else if(flags & Device::DATA)
|
||||||
|
return Device::DATA;
|
||||||
|
else if(flags & Device::ROW)
|
||||||
|
return Device::ROW;
|
||||||
else
|
else
|
||||||
return CartDebug::NONE;
|
return Device::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void CartDebug::disasmTypeAsString(ostream& buf, DisasmType type) const
|
void CartDebug::AccessTypeAsString(ostream& buf, Device::AccessType type) const
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case CartDebug::CODE: buf << "CODE"; break;
|
case Device::CODE: buf << "CODE"; break;
|
||||||
case CartDebug::TCODE: buf << "TCODE"; break;
|
case Device::TCODE: buf << "TCODE"; break;
|
||||||
case CartDebug::GFX: buf << "GFX"; break;
|
case Device::GFX: buf << "GFX"; break;
|
||||||
case CartDebug::PGFX: buf << "PGFX"; break;
|
case Device::PGFX: buf << "PGFX"; break;
|
||||||
case CartDebug::DATA: buf << "DATA"; break;
|
case Device::COL: buf << "COL"; break;
|
||||||
case CartDebug::ROW: buf << "ROW"; break;
|
case Device::PCOL: buf << "PCOL"; break;
|
||||||
case CartDebug::REFERENCED:
|
case Device::BCOL: buf << "BCOL"; break;
|
||||||
case CartDebug::VALID_ENTRY:
|
case Device::AUD: buf << "AUD"; break;
|
||||||
case CartDebug::NONE: break;
|
case Device::DATA: buf << "DATA"; break;
|
||||||
|
case Device::ROW: buf << "ROW"; break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void CartDebug::disasmTypeAsString(ostream& buf, uInt8 flags) const
|
void CartDebug::AccessTypeAsString(ostream& buf, Device::AccessFlags flags) const
|
||||||
{
|
{
|
||||||
if(flags)
|
if(flags)
|
||||||
{
|
{
|
||||||
if(flags & CartDebug::CODE)
|
if(flags & Device::CODE)
|
||||||
buf << "CODE ";
|
buf << "CODE ";
|
||||||
if(flags & CartDebug::TCODE)
|
if(flags & Device::TCODE)
|
||||||
buf << "TCODE ";
|
buf << "TCODE ";
|
||||||
if(flags & CartDebug::GFX)
|
if(flags & Device::GFX)
|
||||||
buf << "GFX ";
|
buf << "GFX ";
|
||||||
if(flags & CartDebug::PGFX)
|
if(flags & Device::PGFX)
|
||||||
buf << "PGFX ";
|
buf << "PGFX ";
|
||||||
if(flags & CartDebug::DATA)
|
if(flags & Device::COL)
|
||||||
|
buf << "COL ";
|
||||||
|
if(flags & Device::PCOL)
|
||||||
|
buf << "PCOL ";
|
||||||
|
if(flags & Device::BCOL)
|
||||||
|
buf << "BCOL ";
|
||||||
|
if(flags & Device::AUD)
|
||||||
|
buf << "AUD ";
|
||||||
|
if(flags & Device::DATA)
|
||||||
buf << "DATA ";
|
buf << "DATA ";
|
||||||
if(flags & CartDebug::ROW)
|
if(flags & Device::ROW)
|
||||||
buf << "ROW ";
|
buf << "ROW ";
|
||||||
if(flags & CartDebug::REFERENCED)
|
if(flags & Device::REFERENCED)
|
||||||
buf << "*REFERENCED ";
|
buf << "*REFERENCED ";
|
||||||
if(flags & CartDebug::VALID_ENTRY)
|
if(flags & Device::VALID_ENTRY)
|
||||||
buf << "*VALID_ENTRY ";
|
buf << "*VALID_ENTRY ";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -31,6 +31,7 @@ using CartMethod = int (CartDebug::*)();
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "DebuggerSystem.hxx"
|
#include "DebuggerSystem.hxx"
|
||||||
|
#include "Device.hxx"
|
||||||
|
|
||||||
class CartState : public DebuggerState
|
class CartState : public DebuggerState
|
||||||
{
|
{
|
||||||
|
@ -47,30 +48,8 @@ class CartDebug : public DebuggerSystem
|
||||||
friend class DiStella;
|
friend class DiStella;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum DisasmType {
|
|
||||||
NONE = 0,
|
|
||||||
REFERENCED = 1 << 0, /* 0x01, code somewhere in the program references it,
|
|
||||||
i.e. LDA $F372 referenced $F372 */
|
|
||||||
VALID_ENTRY = 1 << 1, /* 0x02, 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, // 0x80, disassemble-able code segments
|
|
||||||
TCODE = 1 << 6, // 0x40, (tentative) disassemble-able code segments
|
|
||||||
GFX = 1 << 5, // 0x20, addresses loaded into GRPx registers
|
|
||||||
PGFX = 1 << 4, // 0x10, addresses loaded into PFx registers
|
|
||||||
DATA = 1 << 3, // 0x08, addresses loaded into registers other than GRPx / PFx
|
|
||||||
ROW = 1 << 2, // 0x04, all other addresses
|
|
||||||
// special type for poke()
|
|
||||||
WRITE = TCODE // 0x40, address written to
|
|
||||||
};
|
|
||||||
struct DisassemblyTag {
|
struct DisassemblyTag {
|
||||||
DisasmType type{NONE};
|
Device::AccessType type{Device::NONE};
|
||||||
uInt16 address{0};
|
uInt16 address{0};
|
||||||
string label;
|
string label;
|
||||||
string disasm;
|
string disasm;
|
||||||
|
@ -104,28 +83,55 @@ class CartDebug : public DebuggerSystem
|
||||||
CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
|
CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
|
||||||
void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; }
|
void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; }
|
||||||
|
|
||||||
|
|
||||||
|
// Return the address of the last CPU read
|
||||||
|
int lastReadAddress();
|
||||||
|
// Return the address of the last CPU write
|
||||||
|
int lastWriteAddress();
|
||||||
|
|
||||||
// Return the base (= non-mirrored) address of the last CPU read
|
// Return the base (= non-mirrored) address of the last CPU read
|
||||||
int lastReadBaseAddress();
|
int lastReadBaseAddress();
|
||||||
// Return the base (= non-mirrored) address of the last CPU write
|
// Return the base (= non-mirrored) address of the last CPU write
|
||||||
int lastWriteBaseAddress();
|
int lastWriteBaseAddress();
|
||||||
|
|
||||||
// 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
|
Disassemble from the given address and its bank using the Distella disassembler
|
||||||
|
Address-to-label mappings (and vice-versa) are also determined here
|
||||||
|
|
||||||
|
@param address The address to start with
|
||||||
|
@param force Force a re-disassembly, even if the state hasn't changed
|
||||||
|
|
||||||
|
@return True if disassembly changed from previous call, else false
|
||||||
|
*/
|
||||||
|
bool disassembleAddr(uInt16 address, bool force = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Disassemble from the current PC and its bank using the Distella disassembler
|
||||||
Address-to-label mappings (and vice-versa) are also determined here
|
Address-to-label mappings (and vice-versa) are also determined here
|
||||||
|
|
||||||
@param force Force a re-disassembly, even if the state hasn't changed
|
@param force Force a re-disassembly, even if the state hasn't changed
|
||||||
|
|
||||||
@return True if disassembly changed from previous call, else false
|
@return True if disassembly changed from previous call, else false
|
||||||
*/
|
*/
|
||||||
bool disassemble(bool force = false);
|
bool disassemblePC(bool force = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Disassemble the given bank using the Distella disassembler
|
||||||
|
Address-to-label mappings (and vice-versa) are also determined here
|
||||||
|
|
||||||
|
@param bank The bank to disassemble
|
||||||
|
|
||||||
|
@return True if disassembly changed from previous call, else false
|
||||||
|
*/
|
||||||
|
bool disassembleBank(int bank);
|
||||||
|
|
||||||
|
|
||||||
|
// First, a call is made to disassemble(), which updates the disassembly
|
||||||
|
// list, is required; it will figure out when an actual complete
|
||||||
|
// disassembly is required, and when the previous results can be used
|
||||||
|
//
|
||||||
|
// Later, successive calls to disassembly() simply return the
|
||||||
|
// previous results; no disassembly is done in this case
|
||||||
/**
|
/**
|
||||||
Get the results from the most recent call to disassemble()
|
Get the results from the most recent call to disassemble()
|
||||||
*/
|
*/
|
||||||
|
@ -149,7 +155,7 @@ class CartDebug : public DebuggerSystem
|
||||||
|
|
||||||
@return The disassembly represented as a string
|
@return The disassembly represented as a string
|
||||||
*/
|
*/
|
||||||
string disassemble(uInt16 start, uInt16 lines) const;
|
string disassembleLines(uInt16 start, uInt16 lines) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add a directive to the disassembler. Directives are basically overrides
|
Add a directive to the disassembler. Directives are basically overrides
|
||||||
|
@ -157,14 +163,14 @@ class CartDebug : public DebuggerSystem
|
||||||
things can't be automatically determined. For now, these directives
|
things can't be automatically determined. For now, these directives
|
||||||
have exactly the same syntax as in a distella configuration file.
|
have exactly the same syntax as in a distella configuration file.
|
||||||
|
|
||||||
@param type Currently, CODE/DATA/GFX are supported
|
@param type Currently, CODE/DATA/GFX/PGFX/COL/PCOL/BCOL/AUD/ROW are supported
|
||||||
@param start The start address (inclusive) to mark with the given type
|
@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 end The end address (inclusive) to mark with the given type
|
||||||
@param bank Bank to which these directive apply (0 indicated current bank)
|
@param bank Bank to which these directive apply (0 indicated current bank)
|
||||||
|
|
||||||
@return True if directive was added, else false if it was removed
|
@return True if directive was added, else false if it was removed
|
||||||
*/
|
*/
|
||||||
bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end,
|
bool addDirective(Device::AccessType type, uInt16 start, uInt16 end,
|
||||||
int bank = -1);
|
int bank = -1);
|
||||||
|
|
||||||
// The following are convenience methods that query the cartridge object
|
// The following are convenience methods that query the cartridge object
|
||||||
|
@ -181,7 +187,7 @@ class CartDebug : public DebuggerSystem
|
||||||
/**
|
/**
|
||||||
Get the total number of banks supported by the cartridge.
|
Get the total number of banks supported by the cartridge.
|
||||||
*/
|
*/
|
||||||
int bankCount() const;
|
int romBankCount() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add a label and associated address.
|
Add a label and associated address.
|
||||||
|
@ -231,6 +237,11 @@ class CartDebug : public DebuggerSystem
|
||||||
string saveDisassembly();
|
string saveDisassembly();
|
||||||
string saveRom();
|
string saveRom();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Save access counters file
|
||||||
|
*/
|
||||||
|
string saveAccessFile();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Show Distella directives (both set by the user and determined by Distella)
|
Show Distella directives (both set by the user and determined by Distella)
|
||||||
for the given bank (or all banks, if no bank is specified).
|
for the given bank (or all banks, if no bank is specified).
|
||||||
|
@ -249,18 +260,21 @@ class CartDebug : public DebuggerSystem
|
||||||
*/
|
*/
|
||||||
void getCompletions(const char* in, StringList& list) const;
|
void getCompletions(const char* in, StringList& list) const;
|
||||||
|
|
||||||
// Convert given address to corresponding disassembly type and append to buf
|
// Convert given address to corresponding access type and append to buf
|
||||||
void addressTypeAsString(ostream& buf, uInt16 addr) const;
|
void accessTypeAsString(ostream& buf, uInt16 addr) const;
|
||||||
|
|
||||||
|
// Convert access enum type to corresponding string and append to buf
|
||||||
|
void AccessTypeAsString(ostream& buf, Device::AccessType type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using AddrToLabel = std::map<uInt16, string>;
|
using AddrToLabel = std::map<uInt16, string>;
|
||||||
using LabelToAddr = std::map<string, uInt16,
|
using LabelToAddr = std::map<string, uInt16,
|
||||||
std::function<bool(const string&, const string&)>>;
|
std::function<bool(const string&, const string&)>>;
|
||||||
|
|
||||||
using AddrTypeArray = std::array<uInt8, 0x1000>;
|
using AddrTypeArray = std::array<uInt16, 0x1000>;
|
||||||
|
|
||||||
struct DirectiveTag {
|
struct DirectiveTag {
|
||||||
DisasmType type{NONE};
|
Device::AccessType type{Device::NONE};
|
||||||
uInt16 start{0};
|
uInt16 start{0};
|
||||||
uInt16 end{0};
|
uInt16 end{0};
|
||||||
};
|
};
|
||||||
|
@ -290,6 +304,18 @@ class CartDebug : public DebuggerSystem
|
||||||
};
|
};
|
||||||
ReservedEquates myReserved;
|
ReservedEquates myReserved;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Disassemble from the given address using the Distella disassembler
|
||||||
|
Address-to-label mappings (and vice-versa) are also determined here
|
||||||
|
|
||||||
|
@param bank The current bank to disassemble
|
||||||
|
@param PC A program counter to start with
|
||||||
|
@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(int bank, uInt16 PC, bool force = false);
|
||||||
|
|
||||||
// Actually call DiStella to fill the DisassemblyList structure
|
// Actually call DiStella to fill the DisassemblyList structure
|
||||||
// Return whether the search address was actually in the list
|
// Return whether the search address was actually in the list
|
||||||
bool fillDisassemblyList(BankInfo& bankinfo, uInt16 search);
|
bool fillDisassemblyList(BankInfo& bankinfo, uInt16 search);
|
||||||
|
@ -298,15 +324,12 @@ class CartDebug : public DebuggerSystem
|
||||||
// based on its disassembly
|
// based on its disassembly
|
||||||
void getBankDirectives(ostream& buf, BankInfo& info) const;
|
void getBankDirectives(ostream& buf, BankInfo& info) const;
|
||||||
|
|
||||||
// Get disassembly enum type from 'flags', taking precendence into account
|
// Get access enum type from 'flags', taking precendence into account
|
||||||
DisasmType disasmTypeAbsolute(uInt8 flags) const;
|
Device::AccessType accessTypeAbsolute(Device::AccessFlags flags) const;
|
||||||
|
|
||||||
// Convert disassembly enum type to corresponding string and append to buf
|
// Convert all access types in 'flags' to corresponding string and
|
||||||
void disasmTypeAsString(ostream& buf, DisasmType type) const;
|
|
||||||
|
|
||||||
// Convert all disassembly types in 'flags' to corresponding string and
|
|
||||||
// append to buf
|
// append to buf
|
||||||
void disasmTypeAsString(ostream& buf, uInt8 flags) const;
|
void AccessTypeAsString(ostream& buf, Device::AccessFlags flags) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const OSystem& myOSystem;
|
const OSystem& myOSystem;
|
||||||
|
@ -346,9 +369,6 @@ class CartDebug : public DebuggerSystem
|
||||||
// The maximum length of all labels currently defined
|
// The maximum length of all labels currently defined
|
||||||
uInt16 myLabelLength{8}; // longest pre-defined label
|
uInt16 myLabelLength{8}; // longest pre-defined label
|
||||||
|
|
||||||
// Filenames to use for various I/O (currently these are hardcoded)
|
|
||||||
string myListFile, mySymbolFile, myCfgFile, myDisasmFile, myRomFile;
|
|
||||||
|
|
||||||
/// Table of instruction mnemonics
|
/// Table of instruction mnemonics
|
||||||
static std::array<const char*, 16> ourTIAMnemonicR; // read mode
|
static std::array<const char*, 16> ourTIAMnemonicR; // read mode
|
||||||
static std::array<const char*, 64> ourTIAMnemonicW; // write mode
|
static std::array<const char*, 64> ourTIAMnemonicW; // write mode
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "M6502.hxx"
|
#include "M6502.hxx"
|
||||||
#include "System.hxx"
|
#include "System.hxx"
|
||||||
#include "Debugger.hxx"
|
#include "Debugger.hxx"
|
||||||
#include "CartDebug.hxx"
|
|
||||||
#include "TIADebug.hxx"
|
#include "TIADebug.hxx"
|
||||||
|
|
||||||
#include "CpuDebug.hxx"
|
#include "CpuDebug.hxx"
|
||||||
|
@ -46,6 +45,7 @@ const DebuggerState& CpuDebug::getState()
|
||||||
myState.srcA = my6502.lastSrcAddressA();
|
myState.srcA = my6502.lastSrcAddressA();
|
||||||
myState.srcX = my6502.lastSrcAddressX();
|
myState.srcX = my6502.lastSrcAddressX();
|
||||||
myState.srcY = my6502.lastSrcAddressY();
|
myState.srcY = my6502.lastSrcAddressY();
|
||||||
|
myState.dest = my6502.lastWriteAddress();
|
||||||
|
|
||||||
Debugger::set_bits(myState.PS, myState.PSbits);
|
Debugger::set_bits(myState.PS, myState.PSbits);
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ void CpuDebug::saveOldState()
|
||||||
myOldState.srcA = my6502.lastSrcAddressA();
|
myOldState.srcA = my6502.lastSrcAddressA();
|
||||||
myOldState.srcX = my6502.lastSrcAddressX();
|
myOldState.srcX = my6502.lastSrcAddressX();
|
||||||
myOldState.srcY = my6502.lastSrcAddressY();
|
myOldState.srcY = my6502.lastSrcAddressY();
|
||||||
|
myOldState.dest = my6502.lastWriteAddress();
|
||||||
|
|
||||||
Debugger::set_bits(myOldState.PS, myOldState.PSbits);
|
Debugger::set_bits(myOldState.PS, myOldState.PSbits);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ class CpuState : public DebuggerState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int PC{0}, SP{0}, PS{0}, A{0}, X{0}, Y{0};
|
int PC{0}, SP{0}, PS{0}, A{0}, X{0}, Y{0};
|
||||||
int srcS{0}, srcA{0}, srcX{0}, srcY{0};
|
int srcS{0}, srcA{0}, srcX{0}, srcY{0}, dest{0};
|
||||||
BoolArray PSbits;
|
BoolArray PSbits;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,8 @@ void Debugger::initialize()
|
||||||
FBInitStatus Debugger::initializeVideo()
|
FBInitStatus Debugger::initializeVideo()
|
||||||
{
|
{
|
||||||
string title = string("Stella ") + STELLA_VERSION + ": Debugger mode";
|
string title = string("Stella ") + STELLA_VERSION + ": Debugger mode";
|
||||||
return myOSystem.frameBuffer().createDisplay(title, myWidth, myHeight);
|
return myOSystem.frameBuffer().createDisplay(title, FrameBuffer::BufferType::Debugger,
|
||||||
|
myWidth, myHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -166,7 +167,7 @@ string Debugger::autoExec(StringList* history)
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
|
|
||||||
// autoexec.script is always run
|
// autoexec.script is always run
|
||||||
FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.script");
|
FilesystemNode autoexec(myOSystem.baseDir().getPath() + "autoexec.script");
|
||||||
buf << "autoExec():" << endl
|
buf << "autoExec():" << endl
|
||||||
<< myParser->exec(autoexec, history) << endl;
|
<< myParser->exec(autoexec, history) << endl;
|
||||||
|
|
||||||
|
@ -292,8 +293,9 @@ void Debugger::loadAllStates()
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int Debugger::step()
|
int Debugger::step(bool save)
|
||||||
{
|
{
|
||||||
|
if(save)
|
||||||
saveOldState();
|
saveOldState();
|
||||||
|
|
||||||
uInt64 startCycle = mySystem.cycles();
|
uInt64 startCycle = mySystem.cycles();
|
||||||
|
@ -302,6 +304,7 @@ int Debugger::step()
|
||||||
myOSystem.console().tia().updateScanlineByStep().flushLineCache();
|
myOSystem.console().tia().updateScanlineByStep().flushLineCache();
|
||||||
lockSystem();
|
lockSystem();
|
||||||
|
|
||||||
|
if(save)
|
||||||
addState("step");
|
addState("step");
|
||||||
return int(mySystem.cycles() - startCycle);
|
return int(mySystem.cycles() - startCycle);
|
||||||
}
|
}
|
||||||
|
@ -440,19 +443,19 @@ bool Debugger::writeTrap(uInt16 t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt8 Debugger::peek(uInt16 addr, uInt8 flags)
|
uInt8 Debugger::peek(uInt16 addr, Device::AccessFlags flags)
|
||||||
{
|
{
|
||||||
return mySystem.peek(addr, flags);
|
return mySystem.peek(addr, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt16 Debugger::dpeek(uInt16 addr, uInt8 flags)
|
uInt16 Debugger::dpeek(uInt16 addr, Device::AccessFlags flags)
|
||||||
{
|
{
|
||||||
return uInt16(mySystem.peek(addr, flags) | (mySystem.peek(addr+1, flags) << 8));
|
return uInt16(mySystem.peek(addr, flags) | (mySystem.peek(addr+1, flags) << 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Debugger::poke(uInt16 addr, uInt8 value, uInt8 flags)
|
void Debugger::poke(uInt16 addr, uInt8 value, Device::AccessFlags flags)
|
||||||
{
|
{
|
||||||
mySystem.poke(addr, value, flags);
|
mySystem.poke(addr, value, flags);
|
||||||
}
|
}
|
||||||
|
@ -464,26 +467,26 @@ M6502& Debugger::m6502() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int Debugger::peekAsInt(int addr, uInt8 flags)
|
int Debugger::peekAsInt(int addr, Device::AccessFlags flags)
|
||||||
{
|
{
|
||||||
return mySystem.peek(uInt16(addr), flags);
|
return mySystem.peek(uInt16(addr), flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int Debugger::dpeekAsInt(int addr, uInt8 flags)
|
int Debugger::dpeekAsInt(int addr, Device::AccessFlags flags)
|
||||||
{
|
{
|
||||||
return mySystem.peek(uInt16(addr), flags) |
|
return mySystem.peek(uInt16(addr), flags) |
|
||||||
(mySystem.peek(uInt16(addr+1), flags) << 8);
|
(mySystem.peek(uInt16(addr+1), flags) << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int Debugger::getAccessFlags(uInt16 addr) const
|
Device::AccessFlags Debugger::getAccessFlags(uInt16 addr) const
|
||||||
{
|
{
|
||||||
return mySystem.getAccessFlags(addr);
|
return mySystem.getAccessFlags(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Debugger::setAccessFlags(uInt16 addr, uInt8 flags)
|
void Debugger::setAccessFlags(uInt16 addr, Device::AccessFlags flags)
|
||||||
{
|
{
|
||||||
mySystem.setAccessFlags(addr, flags);
|
mySystem.setAccessFlags(addr, flags);
|
||||||
}
|
}
|
||||||
|
@ -688,6 +691,7 @@ void Debugger::setStartState()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Debugger::setQuitState()
|
void Debugger::setQuitState()
|
||||||
{
|
{
|
||||||
|
myDialog->saveConfig();
|
||||||
saveOldState();
|
saveOldState();
|
||||||
|
|
||||||
// Bus must be unlocked for normal operation when leaving debugger mode
|
// Bus must be unlocked for normal operation when leaving debugger mode
|
||||||
|
@ -873,7 +877,7 @@ std::array<Debugger::BuiltinFunction, 18> Debugger::ourBuiltinFunctions = { {
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// Names are defined here, but processed in YaccParser
|
// Names are defined here, but processed in YaccParser
|
||||||
std::array<Debugger::PseudoRegister, 11> Debugger::ourPseudoRegisters = { {
|
std::array<Debugger::PseudoRegister, 12> Debugger::ourPseudoRegisters = { {
|
||||||
// Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = {
|
// Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = {
|
||||||
{ "_bank", "Currently selected bank" },
|
{ "_bank", "Currently selected bank" },
|
||||||
{ "_cclocks", "Color clocks on current scanline" },
|
{ "_cclocks", "Color clocks on current scanline" },
|
||||||
|
@ -883,10 +887,13 @@ std::array<Debugger::PseudoRegister, 11> Debugger::ourPseudoRegisters = { {
|
||||||
{ "_fcycles", "Number of cycles since frame started" },
|
{ "_fcycles", "Number of cycles since frame started" },
|
||||||
{ "_icycles", "Number of cycles of last instruction" },
|
{ "_icycles", "Number of cycles of last instruction" },
|
||||||
{ "_scan", "Current scanline count" },
|
{ "_scan", "Current scanline count" },
|
||||||
|
{ "_scanend", "Scanline count at end of last frame" },
|
||||||
{ "_scycles", "Number of cycles in current scanline" },
|
{ "_scycles", "Number of cycles in current scanline" },
|
||||||
{ "_vblank", "Whether vertical blank is enabled (1 or 0)" },
|
{ "_vblank", "Whether vertical blank is enabled (1 or 0)" },
|
||||||
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" }
|
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" }
|
||||||
// CPU address access functions:
|
// CPU address access functions:
|
||||||
/*{ "__lastread", "last CPU read address" },
|
/*{ "_lastread", "last CPU read address" },
|
||||||
{ "__lastwrite", "last CPU write address" },*/
|
{ "_lastwrite", "last CPU write address" },
|
||||||
|
{ "__lastbaseread", "last CPU read base address" },
|
||||||
|
{ "__lastbasewrite", "last CPU write base address" }*/
|
||||||
} };
|
} };
|
||||||
|
|
|
@ -47,6 +47,7 @@ class RewindManager;
|
||||||
#include "DialogContainer.hxx"
|
#include "DialogContainer.hxx"
|
||||||
#include "DebuggerDialog.hxx"
|
#include "DebuggerDialog.hxx"
|
||||||
#include "FrameBufferConstants.hxx"
|
#include "FrameBufferConstants.hxx"
|
||||||
|
#include "Cart.hxx"
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,18 +244,18 @@ class Debugger : public DialogContainer
|
||||||
static Debugger& debugger() { return *myStaticDebugger; }
|
static Debugger& debugger() { return *myStaticDebugger; }
|
||||||
|
|
||||||
/** Convenience methods to access peek/poke from System */
|
/** Convenience methods to access peek/poke from System */
|
||||||
uInt8 peek(uInt16 addr, uInt8 flags = 0);
|
uInt8 peek(uInt16 addr, Device::AccessFlags flags = Device::NONE);
|
||||||
uInt16 dpeek(uInt16 addr, uInt8 flags = 0);
|
uInt16 dpeek(uInt16 addr, Device::AccessFlags flags = Device::NONE);
|
||||||
void poke(uInt16 addr, uInt8 value, uInt8 flags = 0);
|
void poke(uInt16 addr, uInt8 value, Device::AccessFlags flags = Device::NONE);
|
||||||
|
|
||||||
/** Convenience method to access the 6502 from System */
|
/** Convenience method to access the 6502 from System */
|
||||||
M6502& m6502() const;
|
M6502& m6502() const;
|
||||||
|
|
||||||
/** These are now exposed so Expressions can use them. */
|
/** These are now exposed so Expressions can use them. */
|
||||||
int peekAsInt(int addr, uInt8 flags = 0);
|
int peekAsInt(int addr, Device::AccessFlags flags = Device::NONE);
|
||||||
int dpeekAsInt(int addr, uInt8 flags = 0);
|
int dpeekAsInt(int addr, Device::AccessFlags flags = Device::NONE);
|
||||||
int getAccessFlags(uInt16 addr) const;
|
Device::AccessFlags getAccessFlags(uInt16 addr) const;
|
||||||
void setAccessFlags(uInt16 addr, uInt8 flags);
|
void setAccessFlags(uInt16 addr, Device::AccessFlags flags);
|
||||||
|
|
||||||
uInt32 getBaseAddress(uInt32 addr, bool read);
|
uInt32 getBaseAddress(uInt32 addr, bool read);
|
||||||
|
|
||||||
|
@ -305,7 +306,7 @@ class Debugger : public DialogContainer
|
||||||
*/
|
*/
|
||||||
void setQuitState();
|
void setQuitState();
|
||||||
|
|
||||||
int step();
|
int step(bool save = true);
|
||||||
int trace();
|
int trace();
|
||||||
void nextScanline(int lines);
|
void nextScanline(int lines);
|
||||||
void nextFrame(int frames);
|
void nextFrame(int frames);
|
||||||
|
@ -362,7 +363,7 @@ class Debugger : public DialogContainer
|
||||||
string name, help;
|
string name, help;
|
||||||
};
|
};
|
||||||
static std::array<BuiltinFunction, 18> ourBuiltinFunctions;
|
static std::array<BuiltinFunction, 18> ourBuiltinFunctions;
|
||||||
static std::array<PseudoRegister, 11> ourPseudoRegisters;
|
static std::array<PseudoRegister, 12> ourPseudoRegisters;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// rewind/unwind n states
|
// rewind/unwind n states
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "M6502.hxx"
|
#include "M6502.hxx"
|
||||||
#include "Expression.hxx"
|
#include "Expression.hxx"
|
||||||
#include "FSNode.hxx"
|
#include "FSNode.hxx"
|
||||||
|
#include "OSystem.hxx"
|
||||||
#include "Settings.hxx"
|
#include "Settings.hxx"
|
||||||
#include "PromptWidget.hxx"
|
#include "PromptWidget.hxx"
|
||||||
#include "RomWidget.hxx"
|
#include "RomWidget.hxx"
|
||||||
|
@ -111,6 +112,8 @@ string DebuggerParser::run(const string& command)
|
||||||
if(validateArgs(i))
|
if(validateArgs(i))
|
||||||
{
|
{
|
||||||
myCommand = i;
|
myCommand = i;
|
||||||
|
if(commands[i].refreshRequired)
|
||||||
|
debugger.baseDialog()->saveConfig();
|
||||||
commands[i].executor(this);
|
commands[i].executor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,9 +132,9 @@ string DebuggerParser::exec(const FilesystemNode& file, StringList* history)
|
||||||
{
|
{
|
||||||
if(file.exists())
|
if(file.exists())
|
||||||
{
|
{
|
||||||
ifstream in(file.getPath());
|
stringstream in;
|
||||||
if(!in.is_open())
|
try { file.read(in); }
|
||||||
return red("script file \'" + file.getShortPath() + "\' not found");
|
catch(...) { return red("script file \'" + file.getShortPath() + "\' not found"); }
|
||||||
|
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -630,11 +633,7 @@ string DebuggerParser::saveScriptFile(string file)
|
||||||
if(file.find_last_of('.') == string::npos)
|
if(file.find_last_of('.') == string::npos)
|
||||||
file += ".script";
|
file += ".script";
|
||||||
|
|
||||||
FilesystemNode node(debugger.myOSystem.defaultSaveDir() + file);
|
stringstream out;
|
||||||
ofstream out(node.getPath());
|
|
||||||
if(!out.is_open())
|
|
||||||
return "Unable to save script to " + node.getShortPath();
|
|
||||||
|
|
||||||
Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
|
Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
|
||||||
for(const auto& f: funcs)
|
for(const auto& f: funcs)
|
||||||
if (!debugger.isBuiltinFunction(f.first))
|
if (!debugger.isBuiltinFunction(f.first))
|
||||||
|
@ -675,9 +674,42 @@ string DebuggerParser::saveScriptFile(string file)
|
||||||
out << endl;
|
out << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilesystemNode node(debugger.myOSystem.defaultSaveDir().getPath() + file);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
node.write(out);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return "Unable to save script to " + node.getShortPath();
|
||||||
|
}
|
||||||
|
|
||||||
return "saved " + node.getShortPath() + " OK";
|
return "saved " + node.getShortPath() + " OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void DebuggerParser::executeDirective(Device::AccessType type)
|
||||||
|
{
|
||||||
|
if(argCount != 2)
|
||||||
|
{
|
||||||
|
outputCommandError("specify start and end of range only", myCommand);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(args[1] < args[0])
|
||||||
|
{
|
||||||
|
commandResult << red("start address must be <= end address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = debugger.cartDebug().addDirective(type, args[0], args[1]);
|
||||||
|
|
||||||
|
commandResult << (result ? "added " : "removed ");
|
||||||
|
debugger.cartDebug().AccessTypeAsString(commandResult, type);
|
||||||
|
commandResult << " directive on range $"
|
||||||
|
<< hex << args[0] << " $" << hex << args[1];
|
||||||
|
debugger.rom().invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// executor methods for commands[] array. All are void, no args.
|
// executor methods for commands[] array. All are void, no args.
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -689,6 +721,13 @@ void DebuggerParser::executeA()
|
||||||
debugger.cpuDebug().setA(uInt8(args[0]));
|
debugger.cpuDebug().setA(uInt8(args[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
// "aud"
|
||||||
|
void DebuggerParser::executeAud()
|
||||||
|
{
|
||||||
|
executeDirective(Device::AUD);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// "base"
|
// "base"
|
||||||
void DebuggerParser::executeBase()
|
void DebuggerParser::executeBase()
|
||||||
|
@ -720,13 +759,20 @@ void DebuggerParser::executeBase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
// "bcol"
|
||||||
|
void DebuggerParser::executeBCol()
|
||||||
|
{
|
||||||
|
executeDirective(Device::BCOL);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// "break"
|
// "break"
|
||||||
void DebuggerParser::executeBreak()
|
void DebuggerParser::executeBreak()
|
||||||
{
|
{
|
||||||
uInt16 addr;
|
uInt16 addr;
|
||||||
uInt8 bank;
|
uInt8 bank;
|
||||||
uInt32 bankCount = debugger.cartDebug().bankCount();
|
uInt32 romBankCount = debugger.cartDebug().romBankCount();
|
||||||
|
|
||||||
if(argCount == 0)
|
if(argCount == 0)
|
||||||
addr = debugger.cpuDebug().pc();
|
addr = debugger.cpuDebug().pc();
|
||||||
|
@ -738,7 +784,7 @@ void DebuggerParser::executeBreak()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bank = args[1];
|
bank = args[1];
|
||||||
if(bank >= bankCount && bank != 0xff)
|
if(bank >= romBankCount && bank != 0xff)
|
||||||
{
|
{
|
||||||
commandResult << red("invalid bank");
|
commandResult << red("invalid bank");
|
||||||
return;
|
return;
|
||||||
|
@ -754,12 +800,12 @@ void DebuggerParser::executeBreak()
|
||||||
commandResult << "cleared";
|
commandResult << "cleared";
|
||||||
|
|
||||||
commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
|
commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
|
||||||
if(bankCount > 1)
|
if(romBankCount > 1)
|
||||||
commandResult << " in bank #" << std::dec << int(bank);
|
commandResult << " in bank #" << std::dec << int(bank);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(int i = 0; i < debugger.cartDebug().bankCount(); ++i)
|
for(int i = 0; i < debugger.cartDebug().romBankCount(); ++i)
|
||||||
{
|
{
|
||||||
bool set = debugger.toggleBreakPoint(addr, i);
|
bool set = debugger.toggleBreakPoint(addr, i);
|
||||||
|
|
||||||
|
@ -772,7 +818,7 @@ void DebuggerParser::executeBreak()
|
||||||
commandResult << "cleared";
|
commandResult << "cleared";
|
||||||
|
|
||||||
commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
|
commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
|
||||||
if(bankCount > 1)
|
if(romBankCount > 1)
|
||||||
commandResult << " in bank #" << std::dec << int(bank);
|
commandResult << " in bank #" << std::dec << int(bank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -912,22 +958,14 @@ void DebuggerParser::executeCls()
|
||||||
// "code"
|
// "code"
|
||||||
void DebuggerParser::executeCode()
|
void DebuggerParser::executeCode()
|
||||||
{
|
{
|
||||||
if(argCount != 2)
|
executeDirective(Device::CODE);
|
||||||
{
|
|
||||||
outputCommandError("specify start and end of range only", myCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(args[1] < args[0])
|
|
||||||
{
|
|
||||||
commandResult << red("start address must be <= end address");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result = debugger.cartDebug().addDirective(
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
CartDebug::CODE, args[0], args[1]);
|
// "col"
|
||||||
commandResult << (result ? "added" : "removed") << " CODE directive on range $"
|
void DebuggerParser::executeCol()
|
||||||
<< hex << args[0] << " $" << hex << args[1];
|
{
|
||||||
debugger.rom().invalidate();
|
executeDirective(Device::COL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -953,22 +991,7 @@ void DebuggerParser::executeD()
|
||||||
// "data"
|
// "data"
|
||||||
void DebuggerParser::executeData()
|
void DebuggerParser::executeData()
|
||||||
{
|
{
|
||||||
if(argCount != 2)
|
executeDirective(Device::DATA);
|
||||||
{
|
|
||||||
outputCommandError("specify start and end of range only", myCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(args[1] < args[0])
|
|
||||||
{
|
|
||||||
commandResult << red("start address must be <= end address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = debugger.cartDebug().addDirective(
|
|
||||||
CartDebug::DATA, args[0], args[1]);
|
|
||||||
commandResult << (result ? "added" : "removed") << " DATA directive on range $"
|
|
||||||
<< hex << args[0] << " $" << hex << args[1];
|
|
||||||
debugger.rom().invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1067,7 +1090,7 @@ void DebuggerParser::executeDisasm()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandResult << debugger.cartDebug().disassemble(start, lines);
|
commandResult << debugger.cartDebug().disassembleLines(start, lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1123,7 +1146,7 @@ void DebuggerParser::executeDump()
|
||||||
file << ".dump";
|
file << ".dump";
|
||||||
FilesystemNode node(file.str());
|
FilesystemNode node(file.str());
|
||||||
// cout << "dump " << args[0] << "-" << args[1] << " to " << file.str() << endl;
|
// cout << "dump " << args[0] << "-" << args[1] << " to " << file.str() << endl;
|
||||||
ofstream ofs(node.getPath(), ofstream::out | ofstream::app);
|
std::ofstream ofs(node.getPath(), std::ofstream::out | std::ofstream::app);
|
||||||
if(!ofs.is_open())
|
if(!ofs.is_open())
|
||||||
{
|
{
|
||||||
outputCommandError("Unable to append dump to file " + node.getShortPath(), myCommand);
|
outputCommandError("Unable to append dump to file " + node.getShortPath(), myCommand);
|
||||||
|
@ -1204,9 +1227,8 @@ void DebuggerParser::executeExec()
|
||||||
if(file.find_last_of('.') == string::npos)
|
if(file.find_last_of('.') == string::npos)
|
||||||
file += ".script";
|
file += ".script";
|
||||||
FilesystemNode node(file);
|
FilesystemNode node(file);
|
||||||
if (!node.exists()) {
|
if (!node.exists())
|
||||||
node = FilesystemNode(debugger.myOSystem.defaultSaveDir() + file);
|
node = FilesystemNode(debugger.myOSystem.defaultSaveDir().getPath() + file);
|
||||||
}
|
|
||||||
|
|
||||||
if (argCount == 2) {
|
if (argCount == 2) {
|
||||||
execPrefix = argStrings[1];
|
execPrefix = argStrings[1];
|
||||||
|
@ -1270,22 +1292,7 @@ void DebuggerParser::executeFunction()
|
||||||
// "gfx"
|
// "gfx"
|
||||||
void DebuggerParser::executeGfx()
|
void DebuggerParser::executeGfx()
|
||||||
{
|
{
|
||||||
if(argCount != 2)
|
executeDirective(Device::GFX);
|
||||||
{
|
|
||||||
outputCommandError("specify start and end of range only", myCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(args[1] < args[0])
|
|
||||||
{
|
|
||||||
commandResult << red("start address must be <= end address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = debugger.cartDebug().addDirective(
|
|
||||||
CartDebug::GFX, args[0], args[1]);
|
|
||||||
commandResult << (result ? "added" : "removed") << " GFX directive on range $"
|
|
||||||
<< hex << args[0] << " $" << hex << args[1];
|
|
||||||
debugger.rom().invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1460,11 +1467,11 @@ void DebuggerParser::executeListbreaks()
|
||||||
{
|
{
|
||||||
stringstream buf;
|
stringstream buf;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
uInt32 bankCount = debugger.cartDebug().bankCount();
|
uInt32 romBankCount = debugger.cartDebug().romBankCount();
|
||||||
|
|
||||||
for(const auto& bp : debugger.breakPoints().getBreakpoints())
|
for(const auto& bp : debugger.breakPoints().getBreakpoints())
|
||||||
{
|
{
|
||||||
if(bankCount == 1)
|
if(romBankCount == 1)
|
||||||
{
|
{
|
||||||
buf << debugger.cartDebug().getLabel(bp.addr, true, 4) << " ";
|
buf << debugger.cartDebug().getLabel(bp.addr, true, 4) << " ";
|
||||||
if(!(++count % 8)) buf << endl;
|
if(!(++count % 8)) buf << endl;
|
||||||
|
@ -1626,26 +1633,18 @@ void DebuggerParser::executePc()
|
||||||
debugger.cpuDebug().setPC(args[0]);
|
debugger.cpuDebug().setPC(args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
// "pcol"
|
||||||
|
void DebuggerParser::executePCol()
|
||||||
|
{
|
||||||
|
executeDirective(Device::PCOL);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// "pgfx"
|
// "pgfx"
|
||||||
void DebuggerParser::executePGfx()
|
void DebuggerParser::executePGfx()
|
||||||
{
|
{
|
||||||
if(argCount != 2)
|
executeDirective(Device::PGFX);
|
||||||
{
|
|
||||||
outputCommandError("specify start and end of range only", myCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(args[1] < args[0])
|
|
||||||
{
|
|
||||||
commandResult << red("start address must be <= end address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = debugger.cartDebug().addDirective(
|
|
||||||
CartDebug::PGFX, args[0], args[1]);
|
|
||||||
commandResult << (result ? "added" : "removed") << " PGFX directive on range $"
|
|
||||||
<< hex << args[0] << " $" << hex << args[1];
|
|
||||||
debugger.rom().invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1722,22 +1721,7 @@ void DebuggerParser::executeRom()
|
||||||
// "row"
|
// "row"
|
||||||
void DebuggerParser::executeRow()
|
void DebuggerParser::executeRow()
|
||||||
{
|
{
|
||||||
if(argCount != 2)
|
executeDirective(Device::ROW);
|
||||||
{
|
|
||||||
outputCommandError("specify start and end of range only", myCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(args[1] < args[0])
|
|
||||||
{
|
|
||||||
commandResult << red("start address must be <= end address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = debugger.cartDebug().addDirective(
|
|
||||||
CartDebug::ROW, args[0], args[1]);
|
|
||||||
commandResult << (result ? "added" : "removed") << " ROW directive on range $"
|
|
||||||
<< hex << args[0] << " $" << hex << args[1];
|
|
||||||
debugger.rom().invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1756,6 +1740,8 @@ void DebuggerParser::executeRunTo()
|
||||||
const CartDebug& cartdbg = debugger.cartDebug();
|
const CartDebug& cartdbg = debugger.cartDebug();
|
||||||
const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
|
const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
|
||||||
|
|
||||||
|
debugger.saveOldState();
|
||||||
|
|
||||||
uInt32 count = 0, max_iterations = uInt32(list.size());
|
uInt32 count = 0, max_iterations = uInt32(list.size());
|
||||||
|
|
||||||
// Create a progress dialog box to show the progress searching through the
|
// Create a progress dialog box to show the progress searching through the
|
||||||
|
@ -1767,7 +1753,7 @@ void DebuggerParser::executeRunTo()
|
||||||
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
do {
|
do {
|
||||||
debugger.step();
|
debugger.step(false);
|
||||||
|
|
||||||
// Update romlist to point to current PC
|
// Update romlist to point to current PC
|
||||||
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
|
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
|
||||||
|
@ -1799,24 +1785,36 @@ void DebuggerParser::executeRunToPc()
|
||||||
const CartDebug& cartdbg = debugger.cartDebug();
|
const CartDebug& cartdbg = debugger.cartDebug();
|
||||||
const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
|
const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
|
||||||
|
|
||||||
|
debugger.saveOldState();
|
||||||
|
|
||||||
uInt32 count = 0;
|
uInt32 count = 0;
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
constexpr uInt32 max_iterations = 1000000;
|
||||||
|
// Create a progress dialog box to show the progress searching through the
|
||||||
|
// disassembly, since this may be a time-consuming operation
|
||||||
|
ostringstream buf;
|
||||||
|
buf << "RunTo PC searching through " << max_iterations << " instructions";
|
||||||
|
ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
|
||||||
|
progress.setRange(0, max_iterations, 5);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
debugger.step();
|
debugger.step(false);
|
||||||
|
|
||||||
// Update romlist to point to current PC
|
// Update romlist to point to current PC
|
||||||
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
|
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
|
||||||
done = (pcline >= 0) && (list[pcline].address == args[0]);
|
done = (pcline >= 0) && (list[pcline].address == args[0]);
|
||||||
} while(!done && ++count < list.size());
|
progress.setProgress(count);
|
||||||
|
} while(!done && ++count < max_iterations/*list.size()*/);
|
||||||
|
progress.close();
|
||||||
|
|
||||||
if(done)
|
if(done)
|
||||||
commandResult
|
commandResult
|
||||||
<< "set PC to " << Base::HEX4 << args[0] << " in "
|
<< "Set PC to $" << Base::HEX4 << args[0] << " in "
|
||||||
<< dec << count << " disassembled instructions";
|
<< dec << count << " instructions";
|
||||||
else
|
else
|
||||||
commandResult
|
commandResult
|
||||||
<< "PC " << Base::HEX4 << args[0] << " not reached or found in "
|
<< "PC $" << Base::HEX4 << args[0] << " not reached or found in "
|
||||||
<< dec << count << " disassembled instructions";
|
<< dec << count << " instructions";
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1833,6 +1831,13 @@ void DebuggerParser::executeSave()
|
||||||
commandResult << saveScriptFile(argStrings[0]);
|
commandResult << saveScriptFile(argStrings[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
// "saveaccess"
|
||||||
|
void DebuggerParser::executeSaveAccess()
|
||||||
|
{
|
||||||
|
commandResult << debugger.cartDebug().saveAccessFile();
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// "saveconfig"
|
// "saveconfig"
|
||||||
void DebuggerParser::executeSaveconfig()
|
void DebuggerParser::executeSaveconfig()
|
||||||
|
@ -1947,9 +1952,23 @@ void DebuggerParser::executeStepwhile()
|
||||||
}
|
}
|
||||||
Expression* expr = YaccParser::getResult();
|
Expression* expr = YaccParser::getResult();
|
||||||
int ncycles = 0;
|
int ncycles = 0;
|
||||||
|
uInt32 count = 0;
|
||||||
|
constexpr uInt32 max_iterations = 1000000;
|
||||||
|
|
||||||
|
// Create a progress dialog box to show the progress searching through the
|
||||||
|
// disassembly, since this may be a time-consuming operation
|
||||||
|
ostringstream buf;
|
||||||
|
buf << "stepwhile running through " << max_iterations << " disassembled instructions";
|
||||||
|
ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
|
||||||
|
progress.setRange(0, max_iterations, 5);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ncycles += debugger.step();
|
ncycles += debugger.step(false);
|
||||||
} while (expr->evaluate());
|
|
||||||
|
progress.setProgress(count);
|
||||||
|
} while (expr->evaluate() && ++count < max_iterations);
|
||||||
|
|
||||||
|
progress.close();
|
||||||
commandResult << "executed " << ncycles << " cycles";
|
commandResult << "executed " << ncycles << " cycles";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2054,18 +2073,18 @@ void DebuggerParser::executeTraps(bool read, bool write, const string& command,
|
||||||
if(read)
|
if(read)
|
||||||
{
|
{
|
||||||
if(beginRead != endRead)
|
if(beginRead != endRead)
|
||||||
conditionBuf << "__lastread>=" << Base::toString(beginRead) << "&&__lastread<=" << Base::toString(endRead);
|
conditionBuf << "__lastbaseread>=" << Base::toString(beginRead) << "&&__lastbaseread<=" << Base::toString(endRead);
|
||||||
else
|
else
|
||||||
conditionBuf << "__lastread==" << Base::toString(beginRead);
|
conditionBuf << "__lastbaseread==" << Base::toString(beginRead);
|
||||||
}
|
}
|
||||||
if(read && write)
|
if(read && write)
|
||||||
conditionBuf << "||";
|
conditionBuf << "||";
|
||||||
if(write)
|
if(write)
|
||||||
{
|
{
|
||||||
if(beginWrite != endWrite)
|
if(beginWrite != endWrite)
|
||||||
conditionBuf << "__lastwrite>=" << Base::toString(beginWrite) << "&&__lastwrite<=" << Base::toString(endWrite);
|
conditionBuf << "__lastbasewrite>=" << Base::toString(beginWrite) << "&&__lastbasewrite<=" << Base::toString(endWrite);
|
||||||
else
|
else
|
||||||
conditionBuf << "__lastwrite==" << Base::toString(beginWrite);
|
conditionBuf << "__lastbasewrite==" << Base::toString(beginWrite);
|
||||||
}
|
}
|
||||||
// parenthesize provided condition (end)
|
// parenthesize provided condition (end)
|
||||||
if(hasCond)
|
if(hasCond)
|
||||||
|
@ -2194,7 +2213,7 @@ void DebuggerParser::executeType()
|
||||||
for(uInt32 i = beg; i <= end; ++i)
|
for(uInt32 i = beg; i <= end; ++i)
|
||||||
{
|
{
|
||||||
commandResult << Base::HEX4 << i << ": ";
|
commandResult << Base::HEX4 << i << ": ";
|
||||||
debugger.cartDebug().addressTypeAsString(commandResult, i);
|
debugger.cartDebug().accessTypeAsString(commandResult, i);
|
||||||
commandResult << endl;
|
commandResult << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2300,7 +2319,7 @@ void DebuggerParser::executeZ()
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// List of all commands available to the parser
|
// List of all commands available to the parser
|
||||||
std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
|
||||||
{
|
{
|
||||||
"a",
|
"a",
|
||||||
"Set Accumulator to <value>",
|
"Set Accumulator to <value>",
|
||||||
|
@ -2311,6 +2330,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||||
std::mem_fn(&DebuggerParser::executeA)
|
std::mem_fn(&DebuggerParser::executeA)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"aud",
|
||||||
|
"Mark 'AUD' range in disassembly",
|
||||||
|
"Start and end of range required\nExample: aud f000 f010",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
{ Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
|
||||||
|
std::mem_fn(&DebuggerParser::executeAud)
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"base",
|
"base",
|
||||||
"Set default number base to <base>",
|
"Set default number base to <base>",
|
||||||
|
@ -2321,6 +2350,17 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||||
std::mem_fn(&DebuggerParser::executeBase)
|
std::mem_fn(&DebuggerParser::executeBase)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"bcol",
|
||||||
|
"Mark 'BCOL' range in disassembly",
|
||||||
|
"Start and end of range required\nExample: bcol f000 f010",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
{ Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
|
||||||
|
std::mem_fn(&DebuggerParser::executeBCol)
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"break",
|
"break",
|
||||||
"Break at <address> and <bank>",
|
"Break at <address> and <bank>",
|
||||||
|
@ -2442,6 +2482,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||||
std::mem_fn(&DebuggerParser::executeCode)
|
std::mem_fn(&DebuggerParser::executeCode)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"col",
|
||||||
|
"Mark 'COL' range in disassembly",
|
||||||
|
"Start and end of range required\nExample: col f000 f010",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
{ Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
|
||||||
|
std::mem_fn(&DebuggerParser::executeCol)
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"colortest",
|
"colortest",
|
||||||
"Show value xx as TIA color",
|
"Show value xx as TIA color",
|
||||||
|
@ -2847,6 +2897,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||||
std::mem_fn(&DebuggerParser::executePc)
|
std::mem_fn(&DebuggerParser::executePc)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"pcol",
|
||||||
|
"Mark 'PCOL' range in disassembly",
|
||||||
|
"Start and end of range required\nExample: col f000 f010",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
{ Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
|
||||||
|
std::mem_fn(&DebuggerParser::executePCol)
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"pgfx",
|
"pgfx",
|
||||||
"Mark 'PGFX' range in disassembly",
|
"Mark 'PGFX' range in disassembly",
|
||||||
|
@ -2980,6 +3040,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||||
std::mem_fn(&DebuggerParser::executeSave)
|
std::mem_fn(&DebuggerParser::executeSave)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"saveaccess",
|
||||||
|
"Save the access counters to CSV file",
|
||||||
|
"Example: saveaccess (no parameters)",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
{ Parameters::ARG_END_ARGS },
|
||||||
|
std::mem_fn(&DebuggerParser::executeSaveAccess)
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"saveconfig",
|
"saveconfig",
|
||||||
"Save Distella config file (with default name)",
|
"Save Distella config file (with default name)",
|
||||||
|
@ -3182,7 +3252,7 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||||
|
|
||||||
{
|
{
|
||||||
"type",
|
"type",
|
||||||
"Show disassembly type for address xx [yy]",
|
"Show access type for address xx [yy]",
|
||||||
"Example: type f000, type f000 f010",
|
"Example: type f000, type f000 f010",
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -27,6 +27,7 @@ class FilesystemNode;
|
||||||
struct Command;
|
struct Command;
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
|
#include "Device.hxx"
|
||||||
|
|
||||||
class DebuggerParser
|
class DebuggerParser
|
||||||
{
|
{
|
||||||
|
@ -97,7 +98,7 @@ class DebuggerParser
|
||||||
std::array<Parameters, 10> parms;
|
std::array<Parameters, 10> parms;
|
||||||
std::function<void (DebuggerParser*)> executor;
|
std::function<void (DebuggerParser*)> executor;
|
||||||
};
|
};
|
||||||
static std::array<Command, 95> commands;
|
static std::array<Command, 100> commands;
|
||||||
|
|
||||||
struct Trap
|
struct Trap
|
||||||
{
|
{
|
||||||
|
@ -140,9 +141,13 @@ class DebuggerParser
|
||||||
// output the error with the example provided for the command
|
// output the error with the example provided for the command
|
||||||
void outputCommandError(const string& errorMsg, int command);
|
void outputCommandError(const string& errorMsg, int command);
|
||||||
|
|
||||||
|
void executeDirective(Device::AccessType type);
|
||||||
|
|
||||||
// List of available command methods
|
// List of available command methods
|
||||||
void executeA();
|
void executeA();
|
||||||
|
void executeAud();
|
||||||
void executeBase();
|
void executeBase();
|
||||||
|
void executeBCol();
|
||||||
void executeBreak();
|
void executeBreak();
|
||||||
void executeBreakif();
|
void executeBreakif();
|
||||||
void executeBreaklabel();
|
void executeBreaklabel();
|
||||||
|
@ -155,6 +160,7 @@ class DebuggerParser
|
||||||
void executeClearwatches();
|
void executeClearwatches();
|
||||||
void executeCls();
|
void executeCls();
|
||||||
void executeCode();
|
void executeCode();
|
||||||
|
void executeCol();
|
||||||
void executeColortest();
|
void executeColortest();
|
||||||
void executeD();
|
void executeD();
|
||||||
void executeData();
|
void executeData();
|
||||||
|
@ -195,6 +201,7 @@ class DebuggerParser
|
||||||
void executeN();
|
void executeN();
|
||||||
void executePalette();
|
void executePalette();
|
||||||
void executePc();
|
void executePc();
|
||||||
|
void executePCol();
|
||||||
void executePGfx();
|
void executePGfx();
|
||||||
void executePrint();
|
void executePrint();
|
||||||
void executeRam();
|
void executeRam();
|
||||||
|
@ -208,6 +215,7 @@ class DebuggerParser
|
||||||
void executeRunToPc();
|
void executeRunToPc();
|
||||||
void executeS();
|
void executeS();
|
||||||
void executeSave();
|
void executeSave();
|
||||||
|
void executeSaveAccess();
|
||||||
void executeSaveallstates();
|
void executeSaveallstates();
|
||||||
void executeSaveconfig();
|
void executeSaveconfig();
|
||||||
void executeSavedisassembly();
|
void executeSavedisassembly();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Debugger.hxx"
|
#include "Debugger.hxx"
|
||||||
|
#include "Device.hxx"
|
||||||
#include "DiStella.hxx"
|
#include "DiStella.hxx"
|
||||||
using Common::Base;
|
using Common::Base;
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ DiStella::DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list,
|
||||||
// Add reserved line equates
|
// Add reserved line equates
|
||||||
ostringstream reservedLabel;
|
ostringstream reservedLabel;
|
||||||
for (int k = 0; k <= myAppData.end; k++) {
|
for (int k = 0; k <= myAppData.end; k++) {
|
||||||
if ((myLabels[k] & (CartDebug::REFERENCED | CartDebug::VALID_ENTRY)) == CartDebug::REFERENCED) {
|
if ((myLabels[k] & (Device::REFERENCED | Device::VALID_ENTRY)) == Device::REFERENCED) {
|
||||||
// If we have a piece of code referenced somewhere else, but cannot
|
// If we have a piece of code referenced somewhere else, but cannot
|
||||||
// locate the label in code (i.e because the address is inside of a
|
// locate the label in code (i.e because the address is inside of a
|
||||||
// multi-byte instruction, then we make note of that address for reference
|
// multi-byte instruction, then we make note of that address for reference
|
||||||
|
@ -107,62 +108,91 @@ void DiStella::disasm(uInt32 distart, int pass)
|
||||||
uInt16 ad;
|
uInt16 ad;
|
||||||
Int32 cycles = 0;
|
Int32 cycles = 0;
|
||||||
AddressingMode addrMode;
|
AddressingMode addrMode;
|
||||||
int labelFound = 0;
|
AddressType labelFound = AddressType::INVALID;
|
||||||
stringstream nextLine, nextLineBytes;
|
stringstream nextLine, nextLineBytes;
|
||||||
|
|
||||||
mySegType = CartDebug::NONE; // create extra lines between code and data
|
mySegType = Device::NONE; // create extra lines between code and data
|
||||||
|
|
||||||
myDisasmBuf.str("");
|
myDisasmBuf.str("");
|
||||||
|
|
||||||
/* pc=myAppData.start; */
|
/* pc=myAppData.start; */
|
||||||
myPC = distart - myOffset;
|
myPC = distart - myOffset;
|
||||||
while (myPC <= myAppData.end) {
|
while(myPC <= myAppData.end)
|
||||||
|
{
|
||||||
// since -1 is used in m6502.m4 for clearing the last peek
|
// since -1 is used in m6502.m4 for clearing the last peek
|
||||||
// and this results into an access at e.g. 0xffff,
|
// and this results into an access at e.g. 0xffff,
|
||||||
// we have to fix the consequences here (ugly!).
|
// we have to fix the consequences here (ugly!).
|
||||||
if(myPC == myAppData.end)
|
if(myPC == myAppData.end)
|
||||||
goto FIX_LAST;
|
goto FIX_LAST;
|
||||||
|
|
||||||
if (checkBits(myPC, CartDebug::GFX | CartDebug::PGFX,
|
if(checkBits(myPC, Device::GFX | Device::PGFX,
|
||||||
CartDebug::CODE)) {
|
Device::CODE))
|
||||||
|
{
|
||||||
if(pass == 2)
|
if(pass == 2)
|
||||||
mark(myPC + myOffset, CartDebug::VALID_ENTRY);
|
mark(myPC + myOffset, Device::VALID_ENTRY);
|
||||||
if(pass == 3)
|
if(pass == 3)
|
||||||
outputGraphics();
|
outputGraphics();
|
||||||
++myPC;
|
++myPC;
|
||||||
} else if (checkBits(myPC, CartDebug::DATA,
|
}
|
||||||
CartDebug::CODE | CartDebug::GFX | CartDebug::PGFX)) {
|
else if(checkBits(myPC, Device::COL | Device::PCOL | Device::BCOL,
|
||||||
|
Device::CODE | Device::GFX | Device::PGFX))
|
||||||
|
{
|
||||||
if(pass == 2)
|
if(pass == 2)
|
||||||
mark(myPC + myOffset, CartDebug::VALID_ENTRY);
|
mark(myPC + myOffset, Device::VALID_ENTRY);
|
||||||
if(pass == 3)
|
if(pass == 3)
|
||||||
outputBytes(CartDebug::DATA);
|
outputColors();
|
||||||
|
++myPC;
|
||||||
|
}
|
||||||
|
else if(checkBits(myPC, Device::AUD,
|
||||||
|
Device::CODE | Device::GFX | Device::PGFX |
|
||||||
|
Device::COL | Device::PCOL | Device::BCOL))
|
||||||
|
{
|
||||||
|
if(pass == 2)
|
||||||
|
mark(myPC + myOffset, Device::VALID_ENTRY);
|
||||||
|
if(pass == 3)
|
||||||
|
outputBytes(Device::AUD);
|
||||||
else
|
else
|
||||||
++myPC;
|
++myPC;
|
||||||
} else if (checkBits(myPC, CartDebug::ROW,
|
}
|
||||||
CartDebug::CODE | CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) {
|
else if(checkBits(myPC, Device::DATA,
|
||||||
|
Device::CODE | Device::GFX | Device::PGFX |
|
||||||
|
Device::COL | Device::PCOL | Device::BCOL |
|
||||||
|
Device::AUD))
|
||||||
|
{
|
||||||
|
if(pass == 2)
|
||||||
|
mark(myPC + myOffset, Device::VALID_ENTRY);
|
||||||
|
if(pass == 3)
|
||||||
|
outputBytes(Device::DATA);
|
||||||
|
else
|
||||||
|
++myPC;
|
||||||
|
}
|
||||||
|
else if(checkBits(myPC, Device::ROW,
|
||||||
|
Device::CODE | Device::GFX | Device::PGFX |
|
||||||
|
Device::COL | Device::PCOL | Device::BCOL |
|
||||||
|
Device::AUD | Device::DATA)) {
|
||||||
FIX_LAST:
|
FIX_LAST:
|
||||||
if(pass == 2)
|
if(pass == 2)
|
||||||
mark(myPC + myOffset, CartDebug::VALID_ENTRY);
|
mark(myPC + myOffset, Device::VALID_ENTRY);
|
||||||
|
|
||||||
if(pass == 3)
|
if(pass == 3)
|
||||||
outputBytes(CartDebug::ROW);
|
outputBytes(Device::ROW);
|
||||||
else
|
else
|
||||||
++myPC;
|
++myPC;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// The following sections must be CODE
|
// The following sections must be CODE
|
||||||
|
|
||||||
// add extra spacing line when switching from non-code to code
|
// add extra spacing line when switching from non-code to code
|
||||||
if (pass == 3 && mySegType != CartDebug::CODE && mySegType != CartDebug::NONE) {
|
if(pass == 3 && mySegType != Device::CODE && mySegType != Device::NONE) {
|
||||||
myDisasmBuf << " ' ' ";
|
myDisasmBuf << " ' ' ";
|
||||||
addEntry(CartDebug::NONE);
|
addEntry(Device::NONE);
|
||||||
mark(myPC + myOffset, CartDebug::REFERENCED); // add label when switching
|
mark(myPC + myOffset, Device::REFERENCED); // add label when switching
|
||||||
}
|
}
|
||||||
mySegType = CartDebug::CODE;
|
mySegType = Device::CODE;
|
||||||
|
|
||||||
/* version 2.1 bug fix */
|
/* version 2.1 bug fix */
|
||||||
if(pass == 2)
|
if(pass == 2)
|
||||||
mark(myPC + myOffset, CartDebug::VALID_ENTRY);
|
mark(myPC + myOffset, Device::VALID_ENTRY);
|
||||||
|
|
||||||
// get opcode
|
// get opcode
|
||||||
opcode = Debugger::debugger().peek(myPC + myOffset);
|
opcode = Debugger::debugger().peek(myPC + myOffset);
|
||||||
|
@ -170,7 +200,7 @@ FIX_LAST:
|
||||||
addrMode = ourLookup[opcode].addr_mode;
|
addrMode = ourLookup[opcode].addr_mode;
|
||||||
|
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
if (checkBit(myPC, CartDebug::REFERENCED))
|
if(checkBit(myPC, Device::REFERENCED))
|
||||||
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
||||||
else
|
else
|
||||||
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
||||||
|
@ -178,14 +208,14 @@ FIX_LAST:
|
||||||
++myPC;
|
++myPC;
|
||||||
|
|
||||||
// detect labels inside instructions (e.g. BIT masks)
|
// detect labels inside instructions (e.g. BIT masks)
|
||||||
labelFound = false;
|
labelFound = AddressType::INVALID;
|
||||||
for(Uint8 i = 0; i < ourLookup[opcode].bytes - 1; i++) {
|
for(Uint8 i = 0; i < ourLookup[opcode].bytes - 1; i++) {
|
||||||
if (checkBit(myPC + i, CartDebug::REFERENCED)) {
|
if(checkBit(myPC + i, Device::REFERENCED)) {
|
||||||
labelFound = true;
|
labelFound = AddressType::ROM;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (labelFound) {
|
if(labelFound != AddressType::INVALID) {
|
||||||
if(myOffset >= 0x1000) {
|
if(myOffset >= 0x1000) {
|
||||||
// the opcode's operand address matches a label address
|
// the opcode's operand address matches a label address
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
|
@ -203,11 +233,12 @@ FIX_LAST:
|
||||||
|
|
||||||
nextLine.str("");
|
nextLine.str("");
|
||||||
cycles = 0;
|
cycles = 0;
|
||||||
addEntry(CartDebug::CODE); // add the new found CODE entry
|
addEntry(Device::CODE); // add the new found CODE entry
|
||||||
}
|
}
|
||||||
// continue with the label's opcode
|
// continue with the label's opcode
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
@ -242,10 +273,10 @@ FIX_LAST:
|
||||||
myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode) << " $"
|
myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode) << " $"
|
||||||
<< Base::HEX4 << myPC + myOffset << "'"
|
<< Base::HEX4 << myPC + myOffset << "'"
|
||||||
<< Base::HEX2 << int(opcode);
|
<< Base::HEX2 << int(opcode);
|
||||||
addEntry(CartDebug::DATA);
|
addEntry(Device::DATA);
|
||||||
|
|
||||||
if(myPC == myAppData.end) {
|
if(myPC == myAppData.end) {
|
||||||
if (checkBit(myPC, CartDebug::REFERENCED))
|
if(checkBit(myPC, Device::REFERENCED))
|
||||||
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
||||||
else
|
else
|
||||||
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
||||||
|
@ -254,7 +285,7 @@ FIX_LAST:
|
||||||
myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode) << " $"
|
myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode) << " $"
|
||||||
<< Base::HEX4 << myPC + myOffset << "'"
|
<< Base::HEX4 << myPC + myOffset << "'"
|
||||||
<< Base::HEX2 << int(opcode);
|
<< Base::HEX2 << int(opcode);
|
||||||
addEntry(CartDebug::DATA);
|
addEntry(Device::DATA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myPCEnd = myAppData.end + myOffset;
|
myPCEnd = myAppData.end + myOffset;
|
||||||
|
@ -271,7 +302,7 @@ FIX_LAST:
|
||||||
/* Line information is already printed, but we can remove the
|
/* Line information is already printed, but we can remove the
|
||||||
Instruction (i.e. BMI) by simply clearing the buffer to print */
|
Instruction (i.e. BMI) by simply clearing the buffer to print */
|
||||||
myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode);
|
myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode);
|
||||||
addEntry(CartDebug::ROW);
|
addEntry(Device::ROW);
|
||||||
nextLine.str("");
|
nextLine.str("");
|
||||||
nextLineBytes.str("");
|
nextLineBytes.str("");
|
||||||
}
|
}
|
||||||
|
@ -299,26 +330,29 @@ FIX_LAST:
|
||||||
case AddressingMode::ABSOLUTE:
|
case AddressingMode::ABSOLUTE:
|
||||||
{
|
{
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
labelFound = mark(ad, CartDebug::REFERENCED);
|
labelFound = mark(ad, Device::REFERENCED);
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
if(ad < 0x100 && mySettings.fFlag)
|
if(ad < 0x100 && mySettings.fFlag)
|
||||||
nextLine << ".w ";
|
nextLine << ".w ";
|
||||||
else
|
else
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
|
|
||||||
if (labelFound == 1) {
|
if(labelFound == AddressType::ROM) {
|
||||||
LABEL_A12_HIGH(ad);
|
LABEL_A12_HIGH(ad);
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
} else if (labelFound == 4) {
|
}
|
||||||
|
else if(labelFound == AddressType::ROM_MIRROR) {
|
||||||
if(mySettings.rFlag) {
|
if(mySettings.rFlag) {
|
||||||
int tmp = (ad & myAppData.end) + myOffset;
|
int tmp = (ad & myAppData.end) + myOffset;
|
||||||
LABEL_A12_HIGH(tmp);
|
LABEL_A12_HIGH(tmp);
|
||||||
nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8);
|
nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
nextLine << "$" << Base::HEX4 << ad;
|
nextLine << "$" << Base::HEX4 << ad;
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
LABEL_A12_LOW(ad);
|
LABEL_A12_LOW(ad);
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
}
|
}
|
||||||
|
@ -329,7 +363,7 @@ FIX_LAST:
|
||||||
case AddressingMode::ZERO_PAGE:
|
case AddressingMode::ZERO_PAGE:
|
||||||
{
|
{
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
labelFound = mark(d1, CartDebug::REFERENCED);
|
labelFound = mark(d1, Device::REFERENCED);
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
LABEL_A12_LOW(int(d1));
|
LABEL_A12_LOW(int(d1));
|
||||||
|
@ -351,34 +385,38 @@ FIX_LAST:
|
||||||
case AddressingMode::ABSOLUTE_X:
|
case AddressingMode::ABSOLUTE_X:
|
||||||
{
|
{
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
labelFound = mark(ad, CartDebug::REFERENCED);
|
labelFound = mark(ad, Device::REFERENCED);
|
||||||
if (pass == 2 && !checkBit(ad & myAppData.end, CartDebug::CODE)) {
|
if(pass == 2 && !checkBit(ad & myAppData.end, Device::CODE)) {
|
||||||
// Since we can't know what address is being accessed unless we also
|
// Since we can't know what address is being accessed unless we also
|
||||||
// know the current X value, this is marked as ROW instead of DATA
|
// know the current X value, this is marked as ROW instead of DATA
|
||||||
// The processing is left here, however, in case future versions of
|
// The processing is left here, however, in case future versions of
|
||||||
// the code can somehow track access to CPU registers
|
// the code can somehow track access to CPU registers
|
||||||
mark(ad, CartDebug::ROW);
|
mark(ad, Device::ROW);
|
||||||
} else if (pass == 3) {
|
}
|
||||||
|
else if(pass == 3) {
|
||||||
if(ad < 0x100 && mySettings.fFlag)
|
if(ad < 0x100 && mySettings.fFlag)
|
||||||
nextLine << ".wx ";
|
nextLine << ".wx ";
|
||||||
else
|
else
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
|
|
||||||
if (labelFound == 1) {
|
if(labelFound == AddressType::ROM) {
|
||||||
LABEL_A12_HIGH(ad);
|
LABEL_A12_HIGH(ad);
|
||||||
nextLine << ",x";
|
nextLine << ",x";
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
} else if (labelFound == 4) {
|
}
|
||||||
|
else if(labelFound == AddressType::ROM_MIRROR) {
|
||||||
if(mySettings.rFlag) {
|
if(mySettings.rFlag) {
|
||||||
int tmp = (ad & myAppData.end) + myOffset;
|
int tmp = (ad & myAppData.end) + myOffset;
|
||||||
LABEL_A12_HIGH(tmp);
|
LABEL_A12_HIGH(tmp);
|
||||||
nextLine << ",x";
|
nextLine << ",x";
|
||||||
nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8);
|
nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
nextLine << "$" << Base::HEX4 << ad << ",x";
|
nextLine << "$" << Base::HEX4 << ad << ",x";
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
LABEL_A12_LOW(ad);
|
LABEL_A12_LOW(ad);
|
||||||
nextLine << ",x";
|
nextLine << ",x";
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
|
@ -390,34 +428,38 @@ FIX_LAST:
|
||||||
case AddressingMode::ABSOLUTE_Y:
|
case AddressingMode::ABSOLUTE_Y:
|
||||||
{
|
{
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
labelFound = mark(ad, CartDebug::REFERENCED);
|
labelFound = mark(ad, Device::REFERENCED);
|
||||||
if (pass == 2 && !checkBit(ad & myAppData.end, CartDebug::CODE)) {
|
if(pass == 2 && !checkBit(ad & myAppData.end, Device::CODE)) {
|
||||||
// Since we can't know what address is being accessed unless we also
|
// Since we can't know what address is being accessed unless we also
|
||||||
// know the current Y value, this is marked as ROW instead of DATA
|
// know the current Y value, this is marked as ROW instead of DATA
|
||||||
// The processing is left here, however, in case future versions of
|
// The processing is left here, however, in case future versions of
|
||||||
// the code can somehow track access to CPU registers
|
// the code can somehow track access to CPU registers
|
||||||
mark(ad, CartDebug::ROW);
|
mark(ad, Device::ROW);
|
||||||
} else if (pass == 3) {
|
}
|
||||||
|
else if(pass == 3) {
|
||||||
if(ad < 0x100 && mySettings.fFlag)
|
if(ad < 0x100 && mySettings.fFlag)
|
||||||
nextLine << ".wy ";
|
nextLine << ".wy ";
|
||||||
else
|
else
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
|
|
||||||
if (labelFound == 1) {
|
if(labelFound == AddressType::ROM) {
|
||||||
LABEL_A12_HIGH(ad);
|
LABEL_A12_HIGH(ad);
|
||||||
nextLine << ",y";
|
nextLine << ",y";
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
} else if (labelFound == 4) {
|
}
|
||||||
|
else if(labelFound == AddressType::ROM_MIRROR) {
|
||||||
if(mySettings.rFlag) {
|
if(mySettings.rFlag) {
|
||||||
int tmp = (ad & myAppData.end) + myOffset;
|
int tmp = (ad & myAppData.end) + myOffset;
|
||||||
LABEL_A12_HIGH(tmp);
|
LABEL_A12_HIGH(tmp);
|
||||||
nextLine << ",y";
|
nextLine << ",y";
|
||||||
nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8);
|
nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
nextLine << "$" << Base::HEX4 << ad << ",y";
|
nextLine << "$" << Base::HEX4 << ad << ",y";
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
LABEL_A12_LOW(ad);
|
LABEL_A12_LOW(ad);
|
||||||
nextLine << ",y";
|
nextLine << ",y";
|
||||||
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8);
|
||||||
|
@ -455,7 +497,7 @@ FIX_LAST:
|
||||||
case AddressingMode::ZERO_PAGE_X:
|
case AddressingMode::ZERO_PAGE_X:
|
||||||
{
|
{
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
labelFound = mark(d1, CartDebug::REFERENCED);
|
labelFound = mark(d1, Device::REFERENCED);
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
LABEL_A12_LOW(d1);
|
LABEL_A12_LOW(d1);
|
||||||
|
@ -468,7 +510,7 @@ FIX_LAST:
|
||||||
case AddressingMode::ZERO_PAGE_Y:
|
case AddressingMode::ZERO_PAGE_Y:
|
||||||
{
|
{
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
labelFound = mark(d1, CartDebug::REFERENCED);
|
labelFound = mark(d1, Device::REFERENCED);
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
LABEL_A12_LOW(d1);
|
LABEL_A12_LOW(d1);
|
||||||
|
@ -486,12 +528,13 @@ FIX_LAST:
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
ad = ((myPC + Int8(d1)) & 0xfff) + myOffset;
|
ad = ((myPC + Int8(d1)) & 0xfff) + myOffset;
|
||||||
|
|
||||||
labelFound = mark(ad, CartDebug::REFERENCED);
|
labelFound = mark(ad, Device::REFERENCED);
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
if (labelFound == 1) {
|
if(labelFound == AddressType::ROM) {
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
LABEL_A12_HIGH(ad);
|
LABEL_A12_HIGH(ad);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
nextLine << " $" << Base::HEX4 << ad;
|
nextLine << " $" << Base::HEX4 << ad;
|
||||||
|
|
||||||
nextLineBytes << Base::HEX2 << int(d1);
|
nextLineBytes << Base::HEX2 << int(d1);
|
||||||
|
@ -502,25 +545,36 @@ FIX_LAST:
|
||||||
case AddressingMode::ABS_INDIRECT:
|
case AddressingMode::ABS_INDIRECT:
|
||||||
{
|
{
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
labelFound = mark(ad, CartDebug::REFERENCED);
|
labelFound = mark(ad, Device::REFERENCED);
|
||||||
if (pass == 2 && !checkBit(ad & myAppData.end, CartDebug::CODE)) {
|
if(pass == 2 && !checkBit(ad & myAppData.end, Device::CODE)) {
|
||||||
// Since we can't know what address is being accessed unless we also
|
// Since we can't know what address is being accessed unless we also
|
||||||
// know the current X value, this is marked as ROW instead of DATA
|
// know the current X value, this is marked as ROW instead of DATA
|
||||||
// The processing is left here, however, in case future versions of
|
// The processing is left here, however, in case future versions of
|
||||||
// the code can somehow track access to CPU registers
|
// the code can somehow track access to CPU registers
|
||||||
mark(ad, CartDebug::ROW);
|
mark(ad, Device::ROW);
|
||||||
} else if (pass == 3) {
|
}
|
||||||
|
else if(pass == 3) {
|
||||||
if(ad < 0x100 && mySettings.fFlag)
|
if(ad < 0x100 && mySettings.fFlag)
|
||||||
nextLine << ".ind ";
|
nextLine << ".ind ";
|
||||||
else
|
else
|
||||||
nextLine << " ";
|
nextLine << " ";
|
||||||
}
|
}
|
||||||
if (labelFound == 1) {
|
if(labelFound == AddressType::ROM) {
|
||||||
nextLine << "(";
|
nextLine << "(";
|
||||||
LABEL_A12_HIGH(ad);
|
LABEL_A12_HIGH(ad);
|
||||||
nextLine << ")";
|
nextLine << ")";
|
||||||
}
|
}
|
||||||
// TODO - should we consider case 4??
|
else if(labelFound == AddressType::ROM_MIRROR) {
|
||||||
|
nextLine << "(";
|
||||||
|
if(mySettings.rFlag) {
|
||||||
|
int tmp = (ad & myAppData.end) + myOffset;
|
||||||
|
LABEL_A12_HIGH(tmp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LABEL_A12_LOW(ad);
|
||||||
|
}
|
||||||
|
nextLine << ")";
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
nextLine << "(";
|
nextLine << "(";
|
||||||
LABEL_A12_LOW(ad);
|
LABEL_A12_LOW(ad);
|
||||||
|
@ -542,28 +596,29 @@ FIX_LAST:
|
||||||
<< ";" << std::dec << int(ourLookup[opcode].cycles)
|
<< ";" << std::dec << int(ourLookup[opcode].cycles)
|
||||||
<< (addrMode == AddressingMode::RELATIVE ? (ad & 0xf00) != ((myPC + myOffset) & 0xf00) ? "/3!" : "/3 " : " ");
|
<< (addrMode == AddressingMode::RELATIVE ? (ad & 0xf00) != ((myPC + myOffset) & 0xf00) ? "/3!" : "/3 " : " ");
|
||||||
if((opcode == 0x40 || opcode == 0x60 || opcode == 0x4c || opcode == 0x00 // code block end
|
if((opcode == 0x40 || opcode == 0x60 || opcode == 0x4c || opcode == 0x00 // code block end
|
||||||
|| checkBit(myPC, CartDebug::REFERENCED) // referenced address
|
|| checkBit(myPC, Device::REFERENCED) // referenced address
|
||||||
|| (ourLookup[opcode].rw_mode == RWMode::WRITE && d1 == WSYNC)) // strobe WSYNC
|
|| (ourLookup[opcode].rw_mode == RWMode::WRITE && d1 == WSYNC)) // strobe WSYNC
|
||||||
&& cycles > 0) {
|
&& cycles > 0) {
|
||||||
// output cycles for previous code block
|
// output cycles for previous code block
|
||||||
myDisasmBuf << "'= " << std::setw(3) << std::setfill(' ') << std::dec << cycles;
|
myDisasmBuf << "'= " << std::setw(3) << std::setfill(' ') << std::dec << cycles;
|
||||||
cycles = 0;
|
cycles = 0;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
myDisasmBuf << "' ";
|
myDisasmBuf << "' ";
|
||||||
}
|
}
|
||||||
myDisasmBuf << "'" << nextLineBytes.str();
|
myDisasmBuf << "'" << nextLineBytes.str();
|
||||||
|
|
||||||
addEntry(CartDebug::CODE);
|
addEntry(Device::CODE);
|
||||||
if(opcode == 0x40 || opcode == 0x60 || opcode == 0x4c || opcode == 0x00) {
|
if(opcode == 0x40 || opcode == 0x60 || opcode == 0x4c || opcode == 0x00) {
|
||||||
myDisasmBuf << " ' ' ";
|
myDisasmBuf << " ' ' ";
|
||||||
addEntry(CartDebug::NONE);
|
addEntry(Device::NONE);
|
||||||
mySegType = CartDebug::NONE; // prevent extra lines if data follows
|
mySegType = Device::NONE; // prevent extra lines if data follows
|
||||||
}
|
}
|
||||||
|
|
||||||
nextLine.str("");
|
nextLine.str("");
|
||||||
nextLineBytes.str("");
|
nextLineBytes.str("");
|
||||||
}
|
}
|
||||||
}
|
} // CODE
|
||||||
} /* while loop */
|
} /* while loop */
|
||||||
|
|
||||||
/* Just in case we are disassembling outside of the address range, force the myPCEnd to EOF */
|
/* Just in case we are disassembling outside of the address range, force the myPCEnd to EOF */
|
||||||
|
@ -602,20 +657,21 @@ void DiStella::disasmPass1(CartDebug::AddressList& debuggerAddresses)
|
||||||
// Note that this is a 'best-effort' approach, since
|
// Note that this is a 'best-effort' approach, since
|
||||||
// Distella will normally keep going until the end of the
|
// Distella will normally keep going until the end of the
|
||||||
// range or branch is encountered
|
// range or branch is encountered
|
||||||
// However, addresses *specifically* marked as DATA/GFX/PGFX
|
// However, addresses *specifically* marked as DATA/GFX/PGFX/COL/PCOL/BCOL/AUD
|
||||||
// in the emulation core indicate that the CODE range has finished
|
// in the emulation core indicate that the CODE range has finished
|
||||||
// Therefore, we stop at the first such address encountered
|
// Therefore, we stop at the first such address encountered
|
||||||
for (uInt32 k = pcBeg; k <= myPCEnd; ++k) {
|
for (uInt32 k = pcBeg; k <= myPCEnd; ++k) {
|
||||||
if (checkBits(k, CartDebug::CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX,
|
if (checkBits(k, Device::Device::DATA | Device::GFX | Device::PGFX |
|
||||||
CartDebug::CODE)) {
|
Device::COL | Device::PCOL | Device::BCOL | Device::AUD,
|
||||||
|
Device::CODE)) {
|
||||||
//if (Debugger::debugger().getAccessFlags(k) &
|
//if (Debugger::debugger().getAccessFlags(k) &
|
||||||
// (CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) {
|
// (Device::DATA | Device::GFX | Device::PGFX)) {
|
||||||
// TODO: this should never happen, remove when we are sure
|
// TODO: this should never happen, remove when we are sure
|
||||||
// TODO: NOT USED: uInt8 flags = Debugger::debugger().getAccessFlags(k);
|
// TODO: NOT USED: uInt16 flags = Debugger::debugger().getAccessFlags(k);
|
||||||
myPCEnd = k - 1;
|
myPCEnd = k - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mark(k, CartDebug::CODE);
|
mark(k, Device::CODE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,7 +693,7 @@ void DiStella::disasmPass1(CartDebug::AddressList& debuggerAddresses)
|
||||||
while (myAddressQueue.empty() && it != debuggerAddresses.end()) {
|
while (myAddressQueue.empty() && it != debuggerAddresses.end()) {
|
||||||
uInt16 addr = *it;
|
uInt16 addr = *it;
|
||||||
|
|
||||||
if (!checkBit(addr - myOffset, CartDebug::CODE)) {
|
if (!checkBit(addr - myOffset, Device::CODE)) {
|
||||||
myAddressQueue.push(addr);
|
myAddressQueue.push(addr);
|
||||||
++it;
|
++it;
|
||||||
} else // remove this address, it is redundant
|
} else // remove this address, it is redundant
|
||||||
|
@ -647,8 +703,8 @@ void DiStella::disasmPass1(CartDebug::AddressList& debuggerAddresses)
|
||||||
// Stella itself can provide hints on whether an address has ever
|
// Stella itself can provide hints on whether an address has ever
|
||||||
// been referenced as CODE
|
// been referenced as CODE
|
||||||
while (myAddressQueue.empty() && codeAccessPoint <= myAppData.end) {
|
while (myAddressQueue.empty() && codeAccessPoint <= myAppData.end) {
|
||||||
if ((Debugger::debugger().getAccessFlags(codeAccessPoint + myOffset) & CartDebug::CODE)
|
if ((Debugger::debugger().getAccessFlags(codeAccessPoint + myOffset) & Device::CODE)
|
||||||
&& !(myLabels[codeAccessPoint & myAppData.end] & CartDebug::CODE)) {
|
&& !(myLabels[codeAccessPoint & myAppData.end] & Device::CODE)) {
|
||||||
myAddressQueue.push(codeAccessPoint + myOffset);
|
myAddressQueue.push(codeAccessPoint + myOffset);
|
||||||
++codeAccessPoint;
|
++codeAccessPoint;
|
||||||
break;
|
break;
|
||||||
|
@ -660,16 +716,17 @@ void DiStella::disasmPass1(CartDebug::AddressList& debuggerAddresses)
|
||||||
|
|
||||||
for (int k = 0; k <= myAppData.end; k++) {
|
for (int k = 0; k <= myAppData.end; k++) {
|
||||||
// Let the emulation core know about tentative code
|
// Let the emulation core know about tentative code
|
||||||
if (checkBit(k, CartDebug::CODE) &&
|
if (checkBit(k, Device::CODE) &&
|
||||||
!(Debugger::debugger().getAccessFlags(k + myOffset) & CartDebug::CODE)
|
!(Debugger::debugger().getAccessFlags(k + myOffset) & Device::CODE)
|
||||||
&& myOffset != 0) {
|
&& myOffset != 0) {
|
||||||
Debugger::debugger().setAccessFlags(k + myOffset, CartDebug::TCODE);
|
Debugger::debugger().setAccessFlags(k + myOffset, Device::TCODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be ROW / unused bytes
|
// Must be ROW / unused bytes
|
||||||
if (!checkBit(k, CartDebug::CODE | CartDebug::GFX |
|
if (!checkBit(k, Device::CODE | Device::GFX | Device::PGFX |
|
||||||
CartDebug::PGFX | CartDebug::DATA))
|
Device::COL | Device::PCOL | Device::BCOL | Device::AUD |
|
||||||
mark(k + myOffset, CartDebug::ROW);
|
Device::DATA))
|
||||||
|
mark(k + myOffset, Device::ROW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,7 +742,9 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
while (myPC <= myAppData.end) {
|
while (myPC <= myAppData.end) {
|
||||||
|
|
||||||
// abort when we reach non-code areas
|
// abort when we reach non-code areas
|
||||||
if (checkBits(myPC, CartDebug::CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX, CartDebug::CODE)) {
|
if (checkBits(myPC, Device::Device::DATA | Device::GFX | Device::PGFX |
|
||||||
|
Device::COL | Device::PCOL | Device::BCOL | Device::AUD,
|
||||||
|
Device::CODE)) {
|
||||||
myPCEnd = (myPC - 1) + myOffset;
|
myPCEnd = (myPC - 1) + myOffset;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -729,22 +788,22 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
switch (addrMode) {
|
switch (addrMode) {
|
||||||
case AddressingMode::ABSOLUTE:
|
case AddressingMode::ABSOLUTE:
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
mark(ad, CartDebug::REFERENCED);
|
mark(ad, Device::REFERENCED);
|
||||||
// handle JMP/JSR
|
// handle JMP/JSR
|
||||||
if (ourLookup[opcode].source == AccessMode::ADDR) {
|
if (ourLookup[opcode].source == AccessMode::ADDR) {
|
||||||
// do NOT use flags set by debugger, else known CODE will not analyzed statically.
|
// do NOT use flags set by debugger, else known CODE will not analyzed statically.
|
||||||
if (!checkBit(ad & myAppData.end, CartDebug::CODE, false)) {
|
if (!checkBit(ad & myAppData.end, Device::CODE, false)) {
|
||||||
if (ad > 0xfff)
|
if (ad > 0xfff)
|
||||||
myAddressQueue.push((ad & myAppData.end) + myOffset);
|
myAddressQueue.push((ad & myAppData.end) + myOffset);
|
||||||
mark(ad, CartDebug::CODE);
|
mark(ad, Device::CODE);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
mark(ad, CartDebug::DATA);
|
mark(ad, Device::DATA);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::ZERO_PAGE:
|
case AddressingMode::ZERO_PAGE:
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
mark(d1, CartDebug::REFERENCED);
|
mark(d1, Device::REFERENCED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::IMMEDIATE:
|
case AddressingMode::IMMEDIATE:
|
||||||
|
@ -753,12 +812,12 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
|
|
||||||
case AddressingMode::ABSOLUTE_X:
|
case AddressingMode::ABSOLUTE_X:
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
mark(ad, CartDebug::REFERENCED);
|
mark(ad, Device::REFERENCED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::ABSOLUTE_Y:
|
case AddressingMode::ABSOLUTE_Y:
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
mark(ad, CartDebug::REFERENCED);
|
mark(ad, Device::REFERENCED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::INDIRECT_X:
|
case AddressingMode::INDIRECT_X:
|
||||||
|
@ -771,12 +830,12 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
|
|
||||||
case AddressingMode::ZERO_PAGE_X:
|
case AddressingMode::ZERO_PAGE_X:
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
mark(d1, CartDebug::REFERENCED);
|
mark(d1, Device::REFERENCED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::ZERO_PAGE_Y:
|
case AddressingMode::ZERO_PAGE_Y:
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
mark(d1, CartDebug::REFERENCED);
|
mark(d1, Device::REFERENCED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::RELATIVE:
|
case AddressingMode::RELATIVE:
|
||||||
|
@ -785,17 +844,17 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
// indexing into the labels array caused a crash
|
// indexing into the labels array caused a crash
|
||||||
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC;
|
||||||
ad = ((myPC + Int8(d1)) & 0xfff) + myOffset;
|
ad = ((myPC + Int8(d1)) & 0xfff) + myOffset;
|
||||||
mark(ad, CartDebug::REFERENCED);
|
mark(ad, Device::REFERENCED);
|
||||||
// do NOT use flags set by debugger, else known CODE will not analyzed statically.
|
// do NOT use flags set by debugger, else known CODE will not analyzed statically.
|
||||||
if (!checkBit(ad - myOffset, CartDebug::CODE, false)) {
|
if (!checkBit(ad - myOffset, Device::CODE, false)) {
|
||||||
myAddressQueue.push(ad);
|
myAddressQueue.push(ad);
|
||||||
mark(ad, CartDebug::CODE);
|
mark(ad, Device::CODE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddressingMode::ABS_INDIRECT:
|
case AddressingMode::ABS_INDIRECT:
|
||||||
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2;
|
||||||
mark(ad, CartDebug::REFERENCED);
|
mark(ad, Device::REFERENCED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -804,10 +863,10 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
|
|
||||||
// mark BRK vector
|
// mark BRK vector
|
||||||
if (opcode == 0x00) {
|
if (opcode == 0x00) {
|
||||||
ad = Debugger::debugger().dpeek(0xfffe, CartDebug::DATA);
|
ad = Debugger::debugger().dpeek(0xfffe, Device::DATA);
|
||||||
if (!myReserved.breakFound) {
|
if (!myReserved.breakFound) {
|
||||||
myAddressQueue.push(ad);
|
myAddressQueue.push(ad);
|
||||||
mark(ad, CartDebug::CODE);
|
mark(ad, Device::CODE);
|
||||||
myReserved.breakFound = true;
|
myReserved.breakFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -823,7 +882,7 @@ void DiStella::disasmFromAddress(uInt32 distart)
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int DiStella::mark(uInt32 address, uInt8 mask, bool directive)
|
DiStella::AddressType DiStella::mark(uInt32 address, uInt16 mask, bool directive)
|
||||||
{
|
{
|
||||||
/*-----------------------------------------------------------------------
|
/*-----------------------------------------------------------------------
|
||||||
For any given offset and code range...
|
For any given offset and code range...
|
||||||
|
@ -831,44 +890,44 @@ int DiStella::mark(uInt32 address, uInt8 mask, bool directive)
|
||||||
If we're between the offset and the end of the code range, we mark
|
If we're between the offset and the end of the code range, we mark
|
||||||
the bit in the labels array for that data. The labels array is an
|
the bit in the labels array for that data. The labels array is an
|
||||||
array of label info for each code address. If this is the case,
|
array of label info for each code address. If this is the case,
|
||||||
return "1", else...
|
return "ROM", else...
|
||||||
|
|
||||||
We sweep for hardware/system equates, which are valid addresses,
|
We sweep for hardware/system equates, which are valid addresses,
|
||||||
outside the scope of the code/data range. For these, we mark its
|
outside the scope of the code/data range. For these, we mark its
|
||||||
corresponding hardware/system array element, and return "2" or "3"
|
corresponding hardware/system array element, and return "TIA" or "RIOT"
|
||||||
(depending on which system/hardware element was accessed).
|
(depending on which system/hardware element was accessed).
|
||||||
If this was not the case...
|
If this was not the case...
|
||||||
|
|
||||||
Next we check if it is a code "mirror". For the 2600, address ranges
|
Next we check if it is a code "mirror". For the 2600, address ranges
|
||||||
are limited with 13 bits, so other addresses can exist outside of the
|
are limited with 13 bits, so other addresses can exist outside of the
|
||||||
standard code/data range. For these, we mark the element in the "labels"
|
standard code/data range. For these, we mark the element in the "labels"
|
||||||
array that corresponds to the mirrored address, and return "4"
|
array that corresponds to the mirrored address, and return "ROM_MIRROR"
|
||||||
|
|
||||||
If all else fails, it's not a valid address, so return 0.
|
If all else fails, it's not a valid address, so return INVALID.
|
||||||
|
|
||||||
A quick example breakdown for a 2600 4K cart:
|
A quick example breakdown for a 2600 4K cart:
|
||||||
===========================================================
|
===========================================================
|
||||||
$00-$3d = system equates (WSYNC, etc...); return 2.
|
$00-$3d = system equates (WSYNC, etc...); return TIA.
|
||||||
$80-$ff = zero-page RAM (ram_80, etc...); return 5.
|
$80-$ff = zero-page RAM (ram_80, etc...); return ZP_RAM.
|
||||||
$0280-$0297 = system equates (INPT0, etc...); mark the array's element
|
$0280-$0297 = system equates (INPT0, etc...); mark the array's element
|
||||||
with the appropriate bit; return 3.
|
with the appropriate bit; return RIOT.
|
||||||
$1000-$1FFF = mark the code/data array for the mirrored address
|
$1000-$1FFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$3000-$3FFF = mark the code/data array for the mirrored address
|
$3000-$3FFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$5000-$5FFF = mark the code/data array for the mirrored address
|
$5000-$5FFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$7000-$7FFF = mark the code/data array for the mirrored address
|
$7000-$7FFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$9000-$9FFF = mark the code/data array for the mirrored address
|
$9000-$9FFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$B000-$BFFF = mark the code/data array for the mirrored address
|
$B000-$BFFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$D000-$DFFF = mark the code/data array for the mirrored address
|
$D000-$DFFF = mark the code/data array for the mirrored address
|
||||||
with the appropriate bit; return 4.
|
with the appropriate bit; return ROM_MIRROR.
|
||||||
$F000-$FFFF = mark the code/data array for the address
|
$F000-$FFFF = mark the code/data array for the address
|
||||||
with the appropriate bit; return 1.
|
with the appropriate bit; return ROM.
|
||||||
Anything else = invalid, return 0.
|
Anything else = invalid, return INVALID.
|
||||||
===========================================================
|
===========================================================
|
||||||
-----------------------------------------------------------------------*/
|
-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -876,36 +935,43 @@ int DiStella::mark(uInt32 address, uInt8 mask, bool directive)
|
||||||
// of Distella assumed either equates or ROM; it didn't take ZP-RAM into account
|
// of Distella assumed either equates or ROM; it didn't take ZP-RAM into account
|
||||||
CartDebug::AddrType type = myDbg.addressType(address);
|
CartDebug::AddrType type = myDbg.addressType(address);
|
||||||
if(type == CartDebug::AddrType::TIA) {
|
if(type == CartDebug::AddrType::TIA) {
|
||||||
return 2;
|
return AddressType::TIA;
|
||||||
} else if (type == CartDebug::AddrType::IO) {
|
}
|
||||||
return 3;
|
else if(type == CartDebug::AddrType::IO) {
|
||||||
} else if (type == CartDebug::AddrType::ZPRAM && myOffset != 0) {
|
return AddressType::RIOT;
|
||||||
return 5;
|
}
|
||||||
} else if (address >= uInt32(myOffset) && address <= uInt32(myAppData.end + myOffset)) {
|
else if(type == CartDebug::AddrType::ZPRAM && myOffset != 0) {
|
||||||
|
return AddressType::ZP_RAM;
|
||||||
|
}
|
||||||
|
else if(address >= uInt32(myOffset) && address <= uInt32(myAppData.end + myOffset)) {
|
||||||
myLabels[address - myOffset] = myLabels[address - myOffset] | mask;
|
myLabels[address - myOffset] = myLabels[address - myOffset] | mask;
|
||||||
if (directive) myDirectives[address - myOffset] = mask;
|
if(directive)
|
||||||
return 1;
|
myDirectives[address - myOffset] = mask;
|
||||||
} else if (address > 0x1000 && myOffset != 0) // Exclude zero-page accesses
|
return AddressType::ROM;
|
||||||
|
}
|
||||||
|
else if(address > 0x1000 && myOffset != 0) // Exclude zero-page accesses
|
||||||
{
|
{
|
||||||
/* 2K & 4K case */
|
/* 2K & 4K case */
|
||||||
myLabels[address & myAppData.end] = myLabels[address & myAppData.end] | mask;
|
myLabels[address & myAppData.end] = myLabels[address & myAppData.end] | mask;
|
||||||
if (directive) myDirectives[address & myAppData.end] = mask;
|
if(directive)
|
||||||
return 4;
|
myDirectives[address & myAppData.end] = mask;
|
||||||
} else
|
return AddressType::ROM_MIRROR;
|
||||||
return 0;
|
}
|
||||||
|
else
|
||||||
|
return AddressType::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool DiStella::checkBit(uInt16 address, uInt8 mask, bool useDebugger) const
|
bool DiStella::checkBit(uInt16 address, uInt16 mask, bool useDebugger) const
|
||||||
{
|
{
|
||||||
// The REFERENCED and VALID_ENTRY flags are needed for any inspection of
|
// The REFERENCED and VALID_ENTRY flags are needed for any inspection of
|
||||||
// an address
|
// an address
|
||||||
// Since they're set only in the labels array (as the lower two bits),
|
// Since they're set only in the labels array (as the lower two bits),
|
||||||
// they must be included in the other bitfields
|
// they must be included in the other bitfields
|
||||||
uInt8 label = myLabels[address & myAppData.end],
|
uInt16 label = myLabels[address & myAppData.end],
|
||||||
lastbits = label & 0x03,
|
lastbits = label & (Device::REFERENCED | Device::VALID_ENTRY),
|
||||||
directive = myDirectives[address & myAppData.end] & 0xFC,
|
directive = myDirectives[address & myAppData.end] & ~(Device::REFERENCED | Device::VALID_ENTRY),
|
||||||
debugger = Debugger::debugger().getAccessFlags(address | myOffset) & 0xFC;
|
debugger = Debugger::debugger().getAccessFlags(address | myOffset) & ~(Device::REFERENCED | Device::VALID_ENTRY);
|
||||||
|
|
||||||
// Any address marked by a manual directive always takes priority
|
// Any address marked by a manual directive always takes priority
|
||||||
if (directive)
|
if (directive)
|
||||||
|
@ -918,7 +984,8 @@ bool DiStella::checkBit(uInt16 address, uInt8 mask, bool useDebugger) const
|
||||||
return label & mask;
|
return label & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiStella::checkBits(uInt16 address, uInt8 mask, uInt8 notMask, bool useDebugger) const
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool DiStella::checkBits(uInt16 address, uInt16 mask, uInt16 notMask, bool useDebugger) const
|
||||||
{
|
{
|
||||||
return checkBit(address, mask, useDebugger) && !checkBit(address, notMask, useDebugger);
|
return checkBit(address, mask, useDebugger) && !checkBit(address, notMask, useDebugger);
|
||||||
}
|
}
|
||||||
|
@ -943,7 +1010,7 @@ bool DiStella::check_range(uInt16 beg, uInt16 end) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void DiStella::addEntry(CartDebug::DisasmType type)
|
void DiStella::addEntry(Device::AccessType type)
|
||||||
{
|
{
|
||||||
CartDebug::DisassemblyTag tag;
|
CartDebug::DisassemblyTag tag;
|
||||||
|
|
||||||
|
@ -969,7 +1036,7 @@ void DiStella::addEntry(CartDebug::DisasmType type)
|
||||||
if (tag.label == EmptyString) {
|
if (tag.label == EmptyString) {
|
||||||
if (myDisasmBuf.peek() != ' ')
|
if (myDisasmBuf.peek() != ' ')
|
||||||
getline(myDisasmBuf, tag.label, '\'');
|
getline(myDisasmBuf, tag.label, '\'');
|
||||||
else if (mySettings.showAddresses && tag.type == CartDebug::CODE) {
|
else if (mySettings.showAddresses && tag.type == Device::CODE) {
|
||||||
// Have addresses indented, to differentiate from actual labels
|
// Have addresses indented, to differentiate from actual labels
|
||||||
tag.label = " " + Base::toString(tag.address, Base::Fmt::_16_4);
|
tag.label = " " + Base::toString(tag.address, Base::Fmt::_16_4);
|
||||||
tag.hllabel = false;
|
tag.hllabel = false;
|
||||||
|
@ -982,7 +1049,7 @@ void DiStella::addEntry(CartDebug::DisasmType type)
|
||||||
// variable length labels, cycle counts, etc
|
// variable length labels, cycle counts, etc
|
||||||
myDisasmBuf.seekg(11, std::ios::beg);
|
myDisasmBuf.seekg(11, std::ios::beg);
|
||||||
switch (tag.type) {
|
switch (tag.type) {
|
||||||
case CartDebug::CODE:
|
case Device::CODE:
|
||||||
getline(myDisasmBuf, tag.disasm, '\'');
|
getline(myDisasmBuf, tag.disasm, '\'');
|
||||||
getline(myDisasmBuf, tag.ccount, '\'');
|
getline(myDisasmBuf, tag.ccount, '\'');
|
||||||
getline(myDisasmBuf, tag.ctotal, '\'');
|
getline(myDisasmBuf, tag.ctotal, '\'');
|
||||||
|
@ -993,25 +1060,29 @@ void DiStella::addEntry(CartDebug::DisasmType type)
|
||||||
// but it could also indicate that code will *never* be accessed
|
// but it could also indicate that code will *never* be accessed
|
||||||
// Since it is impossible to tell the difference, marking the address
|
// Since it is impossible to tell the difference, marking the address
|
||||||
// in the disassembly at least tells the user about it
|
// in the disassembly at least tells the user about it
|
||||||
if (!(Debugger::debugger().getAccessFlags(tag.address) & CartDebug::CODE)
|
if (!(Debugger::debugger().getAccessFlags(tag.address) & Device::CODE)
|
||||||
&& myOffset != 0) {
|
&& myOffset != 0) {
|
||||||
tag.ccount += " *";
|
tag.ccount += " *";
|
||||||
Debugger::debugger().setAccessFlags(tag.address, CartDebug::TCODE);
|
Debugger::debugger().setAccessFlags(tag.address, Device::TCODE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CartDebug::GFX:
|
|
||||||
case CartDebug::PGFX:
|
case Device::GFX:
|
||||||
|
case Device::PGFX:
|
||||||
|
case Device::COL:
|
||||||
|
case Device::PCOL:
|
||||||
|
case Device::BCOL:
|
||||||
|
case Device::DATA:
|
||||||
|
case Device::AUD:
|
||||||
getline(myDisasmBuf, tag.disasm, '\'');
|
getline(myDisasmBuf, tag.disasm, '\'');
|
||||||
getline(myDisasmBuf, tag.bytes);
|
getline(myDisasmBuf, tag.bytes);
|
||||||
break;
|
break;
|
||||||
case CartDebug::DATA:
|
|
||||||
getline(myDisasmBuf, tag.disasm, '\'');
|
case Device::ROW:
|
||||||
getline(myDisasmBuf, tag.bytes);
|
|
||||||
break;
|
|
||||||
case CartDebug::ROW:
|
|
||||||
getline(myDisasmBuf, tag.disasm);
|
getline(myDisasmBuf, tag.disasm);
|
||||||
break;
|
break;
|
||||||
case CartDebug::NONE:
|
|
||||||
|
case Device::NONE:
|
||||||
default: // should never happen
|
default: // should never happen
|
||||||
tag.disasm = " ";
|
tag.disasm = " ";
|
||||||
break;
|
break;
|
||||||
|
@ -1026,18 +1097,18 @@ DONE_WITH_ADD:
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void DiStella::outputGraphics()
|
void DiStella::outputGraphics()
|
||||||
{
|
{
|
||||||
bool isPGfx = checkBit(myPC, CartDebug::PGFX);
|
bool isPGfx = checkBit(myPC, Device::PGFX);
|
||||||
const string& bitString = isPGfx ? "\x1f" : "\x1e";
|
const string& bitString = isPGfx ? "\x1f" : "\x1e";
|
||||||
uInt8 byte = Debugger::debugger().peek(myPC + myOffset);
|
uInt8 byte = Debugger::debugger().peek(myPC + myOffset);
|
||||||
|
|
||||||
// add extra spacing line when switching from non-graphics to graphics
|
// add extra spacing line when switching from non-graphics to graphics
|
||||||
if (mySegType != CartDebug::GFX && mySegType != CartDebug::NONE) {
|
if (mySegType != Device::GFX && mySegType != Device::NONE) {
|
||||||
myDisasmBuf << " ' ' ";
|
myDisasmBuf << " ' ' ";
|
||||||
addEntry(CartDebug::NONE);
|
addEntry(Device::NONE);
|
||||||
}
|
}
|
||||||
mySegType = CartDebug::GFX;
|
mySegType = Device::GFX;
|
||||||
|
|
||||||
if (checkBit(myPC, CartDebug::REFERENCED))
|
if (checkBit(myPC, Device::REFERENCED))
|
||||||
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
||||||
else
|
else
|
||||||
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
||||||
|
@ -1050,23 +1121,91 @@ void DiStella::outputGraphics()
|
||||||
else
|
else
|
||||||
myDisasmBuf << Base::HEX2 << int(byte);
|
myDisasmBuf << Base::HEX2 << int(byte);
|
||||||
|
|
||||||
addEntry(isPGfx ? CartDebug::PGFX : CartDebug::GFX);
|
addEntry(isPGfx ? Device::PGFX : Device::GFX);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void DiStella::outputBytes(CartDebug::DisasmType type)
|
void DiStella::outputColors()
|
||||||
|
{
|
||||||
|
string NTSC_COLOR[16] = {
|
||||||
|
"BLACK", "YELLOW", "BROWN", "ORANGE",
|
||||||
|
"RED", "MAUVE", "VIOLET", "PURPLE",
|
||||||
|
"BLUE", "BLUE_CYAN", "CYAN", "CYAN_GREEN",
|
||||||
|
"GREEN", "GREEN_YELLOW", "GREEN_BEIGE", "BEIGE"
|
||||||
|
};
|
||||||
|
string PAL_COLOR[16] = {
|
||||||
|
"BLACK0", "BLACK1", "YELLOW", "GREEN_YELLOW",
|
||||||
|
"ORANGE", "GREEN", "RED", "CYAN_GREEN",
|
||||||
|
"MAUVE", "CYAN", "VIOLET", "BLUE_CYAN",
|
||||||
|
"PURPLE", "BLUE", "BLACKE", "BLACKF"
|
||||||
|
};
|
||||||
|
string SECAM_COLOR[8] = {
|
||||||
|
"BLACK", "BLUE", "RED", "PURPLE",
|
||||||
|
"GREEN", "CYAN", "YELLOW", "WHITE"
|
||||||
|
};
|
||||||
|
|
||||||
|
uInt8 byte = Debugger::debugger().peek(myPC + myOffset);
|
||||||
|
|
||||||
|
// add extra spacing line when switching from non-colors to colors
|
||||||
|
if(mySegType != Device::COL && mySegType != Device::NONE)
|
||||||
|
{
|
||||||
|
myDisasmBuf << " ' ' ";
|
||||||
|
addEntry(Device::NONE);
|
||||||
|
}
|
||||||
|
mySegType = Device::COL;
|
||||||
|
|
||||||
|
// output label/address
|
||||||
|
if(checkBit(myPC, Device::REFERENCED))
|
||||||
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'";
|
||||||
|
else
|
||||||
|
myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '";
|
||||||
|
|
||||||
|
// output color
|
||||||
|
string color;
|
||||||
|
|
||||||
|
myDisasmBuf << ".byte ";
|
||||||
|
if(myDbg.myConsole.timing() == ConsoleTiming::ntsc)
|
||||||
|
{
|
||||||
|
color = NTSC_COLOR[byte >> 4];
|
||||||
|
myDisasmBuf << color << "|$" << Base::HEX1 << (byte & 0xf);
|
||||||
|
}
|
||||||
|
else if(myDbg.myConsole.timing() == ConsoleTiming::pal)
|
||||||
|
{
|
||||||
|
color = PAL_COLOR[byte >> 4];
|
||||||
|
myDisasmBuf << color << "|$" << Base::HEX1 << (byte & 0xf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color = SECAM_COLOR[(byte >> 1) & 0x7];
|
||||||
|
myDisasmBuf << "$" << Base::HEX1 << (byte >> 4) << "|" << color;
|
||||||
|
}
|
||||||
|
myDisasmBuf << std::setw(int(16 - color.length())) << std::setfill(' ');
|
||||||
|
|
||||||
|
// output address
|
||||||
|
myDisasmBuf << "; $" << Base::HEX4 << myPC + myOffset << " "
|
||||||
|
<< (checkBit(myPC, Device::COL) ? "(Px)" : checkBit(myPC, Device::PCOL) ? "(PF)" : "(BK)");
|
||||||
|
|
||||||
|
// output color value
|
||||||
|
myDisasmBuf << "'" << Base::HEX2 << int(byte);
|
||||||
|
|
||||||
|
addEntry(checkBit(myPC, Device::COL) ? Device::COL :
|
||||||
|
checkBit(myPC, Device::PCOL) ? Device::PCOL : Device::BCOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void DiStella::outputBytes(Device::AccessType type)
|
||||||
{
|
{
|
||||||
bool isType = true;
|
bool isType = true;
|
||||||
bool referenced = checkBit(myPC, CartDebug::REFERENCED);
|
bool referenced = checkBit(myPC, Device::REFERENCED);
|
||||||
bool lineEmpty = true;
|
bool lineEmpty = true;
|
||||||
int numBytes = 0;
|
int numBytes = 0;
|
||||||
|
|
||||||
// add extra spacing line when switching from non-data to data
|
// add extra spacing line when switching from non-data to data
|
||||||
if (mySegType != CartDebug::DATA && mySegType != CartDebug::NONE) {
|
if (mySegType != Device::DATA && mySegType != Device::NONE) {
|
||||||
myDisasmBuf << " ' ' ";
|
myDisasmBuf << " ' ' ";
|
||||||
addEntry(CartDebug::NONE);
|
addEntry(Device::NONE);
|
||||||
}
|
}
|
||||||
mySegType = CartDebug::DATA;
|
mySegType = Device::DATA;
|
||||||
|
|
||||||
while (isType && myPC <= myAppData.end) {
|
while (isType && myPC <= myAppData.end) {
|
||||||
if (referenced) {
|
if (referenced) {
|
||||||
|
@ -1097,13 +1236,15 @@ void DiStella::outputBytes(CartDebug::DisasmType type)
|
||||||
++myPC;
|
++myPC;
|
||||||
}
|
}
|
||||||
isType = checkBits(myPC, type,
|
isType = checkBits(myPC, type,
|
||||||
CartDebug::CODE | (type != CartDebug::DATA ? CartDebug::DATA : 0) | CartDebug::GFX | CartDebug::PGFX);
|
Device::CODE | (type != Device::DATA ? Device::DATA : 0) |
|
||||||
referenced = checkBit(myPC, CartDebug::REFERENCED);
|
Device::GFX | Device::PGFX |
|
||||||
|
Device::COL | Device::PCOL | Device::BCOL | Device::AUD);
|
||||||
|
referenced = checkBit(myPC, Device::REFERENCED);
|
||||||
}
|
}
|
||||||
if (!lineEmpty)
|
if (!lineEmpty)
|
||||||
addEntry(type);
|
addEntry(type);
|
||||||
/*myDisasmBuf << " ' ' ";
|
/*myDisasmBuf << " ' ' ";
|
||||||
addEntry(CartDebug::NONE);*/
|
addEntry(Device::NONE);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "Base.hxx"
|
#include "Base.hxx"
|
||||||
#include "CartDebug.hxx"
|
#include "CartDebug.hxx"
|
||||||
|
#include "Device.hxx"
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,11 +72,26 @@ class DiStella
|
||||||
CartDebug::AddrTypeArray& directives,
|
CartDebug::AddrTypeArray& directives,
|
||||||
CartDebug::ReservedEquates& reserved);
|
CartDebug::ReservedEquates& reserved);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
Enumeration of the addressing type (RAM, ROM, RIOT, TIA...)
|
||||||
|
*/
|
||||||
|
enum class AddressType : int
|
||||||
|
{
|
||||||
|
INVALID,
|
||||||
|
ROM,
|
||||||
|
TIA,
|
||||||
|
RIOT,
|
||||||
|
ROM_MIRROR,
|
||||||
|
ZP_RAM
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Indicate that a new line of disassembly has been completed
|
// Indicate that a new line of disassembly has been completed
|
||||||
// In the original Distella code, this indicated a new line to be printed
|
// In the original Distella code, this indicated a new line to be printed
|
||||||
// Here, we add a new entry to the DisassemblyList
|
// Here, we add a new entry to the DisassemblyList
|
||||||
void addEntry(CartDebug::DisasmType type);
|
void addEntry(Device::AccessType type);
|
||||||
|
|
||||||
// Process directives given in the list
|
// Process directives given in the list
|
||||||
// Directives are basically the contents of a distella configuration file
|
// Directives are basically the contents of a distella configuration file
|
||||||
|
@ -87,32 +103,32 @@ class DiStella
|
||||||
void disasmFromAddress(uInt32 distart);
|
void disasmFromAddress(uInt32 distart);
|
||||||
|
|
||||||
bool check_range(uInt16 start, uInt16 end) const;
|
bool check_range(uInt16 start, uInt16 end) const;
|
||||||
int mark(uInt32 address, uInt8 mask, bool directive = false);
|
AddressType mark(uInt32 address, uInt16 mask, bool directive = false);
|
||||||
bool checkBit(uInt16 address, uInt8 mask, bool useDebugger = true) const;
|
bool checkBit(uInt16 address, uInt16 mask, bool useDebugger = true) const;
|
||||||
|
bool checkBits(uInt16 address, uInt16 mask, uInt16 notMask, bool useDebugger = true) const;
|
||||||
bool checkBits(uInt16 address, uInt8 mask, uInt8 notMask, bool useDebugger = true) const;
|
|
||||||
void outputGraphics();
|
void outputGraphics();
|
||||||
void outputBytes(CartDebug::DisasmType type);
|
void outputColors();
|
||||||
|
void outputBytes(Device::AccessType type);
|
||||||
|
|
||||||
// Convenience methods to generate appropriate labels
|
// Convenience methods to generate appropriate labels
|
||||||
inline void labelA12High(stringstream& buf, uInt8 op, uInt16 addr, int labfound)
|
inline void labelA12High(stringstream& buf, uInt8 op, uInt16 addr, AddressType labfound)
|
||||||
{
|
{
|
||||||
if(!myDbg.getLabel(buf, addr, true))
|
if(!myDbg.getLabel(buf, addr, true))
|
||||||
buf << "L" << Common::Base::HEX4 << addr;
|
buf << "L" << Common::Base::HEX4 << addr;
|
||||||
}
|
}
|
||||||
inline void labelA12Low(stringstream& buf, uInt8 op, uInt16 addr, int labfound)
|
inline void labelA12Low(stringstream& buf, uInt8 op, uInt16 addr, AddressType labfound)
|
||||||
{
|
{
|
||||||
myDbg.getLabel(buf, addr, ourLookup[op].rw_mode == RWMode::READ, 2);
|
myDbg.getLabel(buf, addr, ourLookup[op].rw_mode == RWMode::READ, 2);
|
||||||
if (labfound == 2)
|
if (labfound == AddressType::TIA)
|
||||||
{
|
{
|
||||||
if(ourLookup[op].rw_mode == RWMode::READ)
|
if(ourLookup[op].rw_mode == RWMode::READ)
|
||||||
myReserved.TIARead[addr & 0x0F] = true;
|
myReserved.TIARead[addr & 0x0F] = true;
|
||||||
else
|
else
|
||||||
myReserved.TIAWrite[addr & 0x3F] = true;
|
myReserved.TIAWrite[addr & 0x3F] = true;
|
||||||
}
|
}
|
||||||
else if (labfound == 3)
|
else if (labfound == AddressType::RIOT)
|
||||||
myReserved.IOReadWrite[(addr & 0xFF) - 0x80] = true;
|
myReserved.IOReadWrite[(addr & 0xFF) - 0x80] = true;
|
||||||
else if (labfound == 5)
|
else if (labfound == AddressType::ZP_RAM)
|
||||||
myReserved.ZPRAM[(addr & 0xFF) - 0x80] = true;
|
myReserved.ZPRAM[(addr & 0xFF) - 0x80] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,80 +16,25 @@
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include "Cart0840.hxx"
|
#include "Cart0840.hxx"
|
||||||
#include "PopUpWidget.hxx"
|
|
||||||
#include "Cart0840Widget.hxx"
|
#include "Cart0840Widget.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Cartridge0840Widget::Cartridge0840Widget(
|
Cartridge0840Widget::Cartridge0840Widget(
|
||||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||||
int x, int y, int w, int h, Cartridge0840& cart)
|
int x, int y, int w, int h, Cartridge0840& cart)
|
||||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||||
myCart(cart)
|
|
||||||
{
|
{
|
||||||
uInt16 size = 2 * 4096;
|
myHotspotDelta = 0x40;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
string Cartridge0840Widget::description()
|
||||||
|
{
|
||||||
ostringstream info;
|
ostringstream info;
|
||||||
info << "0840 ECONObanking, two 4K banks\n"
|
|
||||||
<< "Startup bank = " << cart.startBank() << "\n";
|
|
||||||
|
|
||||||
// Eventually, we should query this from the debugger/disassembler
|
info << "0840 ECONObanking, two 4K banks\n";
|
||||||
for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2;
|
info << CartridgeEnhancedWidget::description();
|
||||||
++i, offset += 0x1000, spot += 0x40)
|
|
||||||
{
|
return info.str();
|
||||||
uInt16 start = uInt16((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 = 2,
|
|
||||||
ypos = addBaseInformation(size, "Fred X. Quimby", info.str()) + myLineHeight;
|
|
||||||
|
|
||||||
VariantList items;
|
|
||||||
VarList::push_back(items, "0 ($800)");
|
|
||||||
VarList::push_back(items, "1 ($840)");
|
|
||||||
myBank =
|
|
||||||
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($800)"),
|
|
||||||
myLineHeight, items, "Set bank ",
|
|
||||||
0, kBankChanged);
|
|
||||||
myBank->setTarget(this);
|
|
||||||
addFocusWidget(myBank);
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void Cartridge0840Widget::loadConfig()
|
|
||||||
{
|
|
||||||
Debugger& dbg = instance().debugger();
|
|
||||||
CartDebug& cart = dbg.cartDebug();
|
|
||||||
const CartState& state = static_cast<const CartState&>(cart.getState());
|
|
||||||
const CartState& oldstate = static_cast<const CartState&>(cart.getOldState());
|
|
||||||
|
|
||||||
myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank);
|
|
||||||
|
|
||||||
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 std::array<string, 2> spot = { "$800", "$840" };
|
|
||||||
buf << "Bank = " << std::dec << myCart.getBank()
|
|
||||||
<< ", hotspot = " << spot[myCart.getBank()];
|
|
||||||
|
|
||||||
return buf.str();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,10 @@
|
||||||
#define CARTRIDGE0840_WIDGET_HXX
|
#define CARTRIDGE0840_WIDGET_HXX
|
||||||
|
|
||||||
class Cartridge0840;
|
class Cartridge0840;
|
||||||
class PopUpWidget;
|
|
||||||
|
|
||||||
#include "CartDebugWidget.hxx"
|
#include "CartEnhancedWidget.hxx"
|
||||||
|
|
||||||
class Cartridge0840Widget : public CartDebugWidget
|
class Cartridge0840Widget : public CartridgeEnhancedWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cartridge0840Widget(GuiObject* boss, const GUI::Font& lfont,
|
Cartridge0840Widget(GuiObject* boss, const GUI::Font& lfont,
|
||||||
|
@ -33,17 +32,11 @@ class Cartridge0840Widget : public CartDebugWidget
|
||||||
virtual ~Cartridge0840Widget() = default;
|
virtual ~Cartridge0840Widget() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Cartridge0840& myCart;
|
string manufacturer() override { return "Fred X. Quimby"; }
|
||||||
PopUpWidget* myBank{nullptr};
|
|
||||||
|
|
||||||
enum { kBankChanged = 'bkCH' };
|
string description() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadConfig() override;
|
|
||||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
|
||||||
|
|
||||||
string bankState() override;
|
|
||||||
|
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
Cartridge0840Widget() = delete;
|
Cartridge0840Widget() = delete;
|
||||||
Cartridge0840Widget(const Cartridge0840Widget&) = delete;
|
Cartridge0840Widget(const Cartridge0840Widget&) = delete;
|
||||||
|
|
|
@ -22,15 +22,18 @@
|
||||||
Cartridge2KWidget::Cartridge2KWidget(
|
Cartridge2KWidget::Cartridge2KWidget(
|
||||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||||
int x, int y, int w, int h, Cartridge2K& cart)
|
int x, int y, int w, int h, Cartridge2K& cart)
|
||||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h)
|
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||||
{
|
{
|
||||||
// Eventually, we should query this from the debugger/disassembler
|
initialize();
|
||||||
size_t size = cart.mySize;
|
}
|
||||||
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
|
|
||||||
start -= start % size;
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
string Cartridge2KWidget::description()
|
||||||
ostringstream info;
|
{
|
||||||
info << "Standard 2K cartridge, non-bankswitched\n"
|
ostringstream info;
|
||||||
<< "Accessible @ $" << Common::Base::HEX4 << start << " - " << "$" << (start + size - 1);
|
|
||||||
addBaseInformation(size, "Atari", info.str());
|
info << "Standard 2K cartridge, non-bankswitched\n";
|
||||||
|
info << CartridgeEnhancedWidget::description();
|
||||||
|
|
||||||
|
return info.str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
|
|
||||||
class Cartridge2K;
|
class Cartridge2K;
|
||||||
|
|
||||||
#include "CartDebugWidget.hxx"
|
#include "CartEnhancedWidget.hxx"
|
||||||
|
|
||||||
class Cartridge2KWidget : public CartDebugWidget
|
class Cartridge2KWidget : public CartridgeEnhancedWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cartridge2KWidget(GuiObject* boss, const GUI::Font& lfont,
|
Cartridge2KWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||||
|
@ -32,10 +32,11 @@ class Cartridge2KWidget : public CartDebugWidget
|
||||||
virtual ~Cartridge2KWidget() = default;
|
virtual ~Cartridge2KWidget() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// No implementation for non-bankswitched ROMs
|
string manufacturer() override { return "Atari"; }
|
||||||
void loadConfig() override { }
|
|
||||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override { }
|
|
||||||
|
|
||||||
|
string description() override;
|
||||||
|
|
||||||
|
private:
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
Cartridge2KWidget() = delete;
|
Cartridge2KWidget() = delete;
|
||||||
Cartridge2KWidget(const Cartridge2KWidget&) = delete;
|
Cartridge2KWidget(const Cartridge2KWidget&) = delete;
|
||||||
|
|
|
@ -18,320 +18,230 @@
|
||||||
#include "Cart3EPlus.hxx"
|
#include "Cart3EPlus.hxx"
|
||||||
#include "EditTextWidget.hxx"
|
#include "EditTextWidget.hxx"
|
||||||
#include "PopUpWidget.hxx"
|
#include "PopUpWidget.hxx"
|
||||||
#include "Cart3EPlusWidget.hxx"
|
#include "CartEnhancedWidget.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Cartridge3EPlusWidget::Cartridge3EPlusWidget(
|
Cartridge3EPlusWidget::Cartridge3EPlusWidget(
|
||||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||||
int x, int y, int w, int h, Cartridge3EPlus& cart)
|
int x, int y, int w, int h, Cartridge3EPlus& cart)
|
||||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart),
|
||||||
myCart(cart)
|
myCart3EP(cart)
|
||||||
{
|
{
|
||||||
size_t size = cart.mySize;
|
initialize();
|
||||||
|
|
||||||
ostringstream info;
|
|
||||||
info << "3EPlus cartridge - (64K ROM + RAM)\n"
|
|
||||||
<< " 4-64K ROM (1K banks), 32K RAM (512b banks)\n"
|
|
||||||
<< "Each 1K ROM selected by writing to $3F\n"
|
|
||||||
"Each 512b RAM selected by writing to $3E\n"
|
|
||||||
" Lower 512b of bank x (R)\n"
|
|
||||||
" Upper 512b of bank x (+$200) (W)\n"
|
|
||||||
<< "Startup bank = 0/-1/-1/0 (ROM)\n";
|
|
||||||
|
|
||||||
// Eventually, we should query this from the debugger/disassembler
|
|
||||||
//uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
|
|
||||||
// Currently the cart starts at bank 0. If we change that, we have to change this too.
|
|
||||||
uInt16 start = (cart.myImage[0x400-3] << 8) | cart.myImage[0x400 - 4];
|
|
||||||
start -= start % 0x1000;
|
|
||||||
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
|
|
||||||
|
|
||||||
int xpos = 2,
|
|
||||||
ypos = addBaseInformation(size, "T. Jentzsch", info.str()) +
|
|
||||||
myLineHeight;
|
|
||||||
|
|
||||||
VariantList bankno;
|
|
||||||
for(uInt32 i = 0; i < myCart.ROM_BANK_COUNT; ++i)
|
|
||||||
VarList::push_back(bankno, i, i);
|
|
||||||
|
|
||||||
VariantList banktype;
|
|
||||||
VarList::push_back(banktype, "ROM", "ROM");
|
|
||||||
VarList::push_back(banktype, "RAM", "RAM");
|
|
||||||
|
|
||||||
for(uInt32 i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
int xpos_s, ypos_s = ypos;
|
|
||||||
|
|
||||||
ostringstream label;
|
|
||||||
label << "Set segment " << i << " as ";
|
|
||||||
|
|
||||||
new StaticTextWidget(boss, _font, xpos, ypos, _font.getStringWidth(label.str()),
|
|
||||||
myFontHeight, label.str(), TextAlign::Left);
|
|
||||||
ypos += myLineHeight + 8;
|
|
||||||
|
|
||||||
xpos += 20;
|
|
||||||
myBankNumber[i] =
|
|
||||||
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("Slot "),
|
|
||||||
myLineHeight, bankno, "Slot ",
|
|
||||||
6*_font.getMaxCharWidth());
|
|
||||||
addFocusWidget(myBankNumber[i]);
|
|
||||||
|
|
||||||
xpos += myBankNumber[i]->getWidth();
|
|
||||||
myBankType[i] =
|
|
||||||
new PopUpWidget(boss, _font, xpos, ypos-2, 5*_font.getMaxCharWidth(),
|
|
||||||
myLineHeight, banktype, " of ", _font.getStringWidth(" of "));
|
|
||||||
addFocusWidget(myBankType[i]);
|
|
||||||
|
|
||||||
xpos += myBankType[i]->getWidth() + 10;
|
|
||||||
|
|
||||||
myBankCommit[i] = new ButtonWidget(boss, _font, xpos, ypos-4,
|
|
||||||
_font.getStringWidth(" Commit "), myButtonHeight,
|
|
||||||
"Commit", bankEnum[i]);
|
|
||||||
myBankCommit[i]->setTarget(this);
|
|
||||||
addFocusWidget(myBankCommit[i]);
|
|
||||||
|
|
||||||
xpos_s = xpos + myBankCommit[i]->getWidth() + 20;
|
|
||||||
|
|
||||||
StaticTextWidget* t;
|
|
||||||
int addr1 = start + (i*0x400), addr2 = addr1 + 0x1FF;
|
|
||||||
|
|
||||||
label.str("");
|
|
||||||
label << Common::Base::HEX4 << addr1 << "-" << Common::Base::HEX4 << addr2;
|
|
||||||
t = new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
|
|
||||||
_font.getStringWidth(label.str()), myFontHeight, label.str(), TextAlign::Left);
|
|
||||||
|
|
||||||
int xoffset = xpos_s+t->getWidth() + 10;
|
|
||||||
myBankState[2*i] = new EditTextWidget(boss, _font, xoffset, ypos_s,
|
|
||||||
w - xoffset - 10, myLineHeight, "");
|
|
||||||
myBankState[2*i]->setEditable(false, true);
|
|
||||||
ypos_s += myLineHeight + 4;
|
|
||||||
|
|
||||||
label.str("");
|
|
||||||
label << Common::Base::HEX4 << (addr2 + 1) << "-" << Common::Base::HEX4 << (addr2 + 1 + 0x1FF);
|
|
||||||
new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
|
|
||||||
_font.getStringWidth(label.str()), myFontHeight, label.str(), TextAlign::Left);
|
|
||||||
|
|
||||||
myBankState[2*i+1] = new EditTextWidget(boss, _font, xoffset, ypos_s,
|
|
||||||
w - xoffset - 10, myLineHeight, "");
|
|
||||||
myBankState[2*i+1]->setEditable(false, true);
|
|
||||||
|
|
||||||
xpos = 10;
|
|
||||||
ypos+= 2 * myLineHeight;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Cartridge3EPlusWidget::saveOldState()
|
string Cartridge3EPlusWidget::description()
|
||||||
{
|
{
|
||||||
myOldState.internalram.clear();
|
ostringstream info;
|
||||||
|
size_t size;
|
||||||
|
const ByteBuffer& image = myCart.getImage(size);
|
||||||
|
uInt16 numRomBanks = myCart.romBankCount();
|
||||||
|
uInt16 numRamBanks = myCart.ramBankCount();
|
||||||
|
|
||||||
for(uInt32 i = 0; i < internalRamSize(); ++i)
|
info << "3E+ cartridge - (4" << ELLIPSIS << "64K ROM + RAM)\n"
|
||||||
myOldState.internalram.push_back(myCart.myRAM[i]);
|
<< " " << numRomBanks << " 1K ROM banks + " << numRamBanks << " 512b RAM banks\n"
|
||||||
|
<< " mapped into four segments\n"
|
||||||
|
"ROM bank & segment selected by writing to $3F\n"
|
||||||
|
"RAM bank & segment selected by writing to $3E\n"
|
||||||
|
" Lower 512b of segment for read access\n"
|
||||||
|
" Upper 512b of segment for write access\n"
|
||||||
|
"Startup bank = -1/-1/-1/0 (ROM)\n";
|
||||||
|
|
||||||
|
// Eventually, we should query this from the debugger/disassembler
|
||||||
|
uInt16 start = (image[0x400 - 3] << 8) | image[0x400 - 4];
|
||||||
|
start -= start % 0x1000;
|
||||||
|
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
|
||||||
|
|
||||||
|
return info.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void Cartridge3EPlusWidget::bankSelect(int& ypos)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
const ByteBuffer& image = myCart.getImage(size);
|
||||||
|
const int VGAP = myFontHeight / 4;
|
||||||
|
VariantList banktype;
|
||||||
|
|
||||||
|
VarList::push_back(banktype, "ROM", "ROM");
|
||||||
|
VarList::push_back(banktype, "RAM", "RAM");
|
||||||
|
|
||||||
|
myBankWidgets = make_unique<PopUpWidget* []>(bankSegs());
|
||||||
|
|
||||||
|
ypos -= VGAP * 2;
|
||||||
|
|
||||||
|
for(uInt32 seg = 0; seg < bankSegs(); ++seg)
|
||||||
|
{
|
||||||
|
int xpos = 2, xpos_s, ypos_s = ypos + 1, width;
|
||||||
|
ostringstream label;
|
||||||
|
VariantList items;
|
||||||
|
|
||||||
|
label << "Set segment " << seg << " as ";
|
||||||
|
|
||||||
|
new StaticTextWidget(_boss, _font, xpos, ypos, label.str());
|
||||||
|
ypos += myLineHeight + VGAP * 2;
|
||||||
|
|
||||||
|
xpos += _font.getMaxCharWidth() * 2;
|
||||||
|
|
||||||
|
CartridgeEnhancedWidget::bankList(std::max(myCart.romBankCount(), myCart.ramBankCount()),
|
||||||
|
seg, items, width);
|
||||||
|
myBankWidgets[seg] =
|
||||||
|
new PopUpWidget(_boss, _font, xpos, ypos - 2, width,
|
||||||
|
myLineHeight, items, "Bank ", 0, kBankChanged);
|
||||||
|
myBankWidgets[seg]->setID(seg);
|
||||||
|
myBankWidgets[seg]->setTarget(this);
|
||||||
|
addFocusWidget(myBankWidgets[seg]);
|
||||||
|
|
||||||
|
xpos += myBankWidgets[seg]->getWidth();
|
||||||
|
myBankType[seg] =
|
||||||
|
new PopUpWidget(_boss, _font, xpos, ypos - 2, 3 * _font.getMaxCharWidth(),
|
||||||
|
myLineHeight, banktype, " of ", 0, kRomRamChanged);
|
||||||
|
myBankType[seg]->setID(seg);
|
||||||
|
myBankType[seg]->setTarget(this);
|
||||||
|
addFocusWidget(myBankType[seg]);
|
||||||
|
|
||||||
|
xpos = myBankType[seg]->getRight() + _font.getMaxCharWidth();
|
||||||
|
|
||||||
|
// add "Commit" button (why required?)
|
||||||
|
myBankCommit[seg] = new ButtonWidget(_boss, _font, xpos, ypos - 4,
|
||||||
|
_font.getStringWidth(" Commit "), myButtonHeight,
|
||||||
|
"Commit", kChangeBank);
|
||||||
|
myBankCommit[seg]->setID(seg);
|
||||||
|
myBankCommit[seg]->setTarget(this);
|
||||||
|
addFocusWidget(myBankCommit[seg]);
|
||||||
|
|
||||||
|
xpos_s = myBankCommit[seg]->getRight() + _font.getMaxCharWidth() * 2;
|
||||||
|
|
||||||
|
StaticTextWidget* t;
|
||||||
|
uInt16 start = (image[0x400 - 3] << 8) | image[0x400 - 4];
|
||||||
|
start -= start % 0x1000;
|
||||||
|
int addr1 = start + (seg * 0x400), addr2 = addr1 + 0x200;
|
||||||
|
|
||||||
|
label.str("");
|
||||||
|
label << "$" << Common::Base::HEX4 << addr1 << "-$" << Common::Base::HEX4 << (addr1 + 0x1FF);
|
||||||
|
t = new StaticTextWidget(_boss, _font, xpos_s, ypos_s + 2, label.str());
|
||||||
|
|
||||||
|
int xoffset = t->getRight() + _font.getMaxCharWidth();
|
||||||
|
myBankState[2 * seg] = new EditTextWidget(_boss, _font, xoffset, ypos_s,
|
||||||
|
_w - xoffset - 10, myLineHeight, "");
|
||||||
|
myBankState[2 * seg]->setEditable(false, true);
|
||||||
|
ypos_s += myLineHeight + VGAP;
|
||||||
|
|
||||||
|
label.str("");
|
||||||
|
label << "$" << Common::Base::HEX4 << addr2 << "-$" << Common::Base::HEX4 << (addr2 + 0x1FF);
|
||||||
|
new StaticTextWidget(_boss, _font, xpos_s, ypos_s + 2, label.str());
|
||||||
|
|
||||||
|
myBankState[2 * seg + 1] = new EditTextWidget(_boss, _font, xoffset, ypos_s,
|
||||||
|
_w - xoffset - 10, myLineHeight, "");
|
||||||
|
myBankState[2 * seg + 1]->setEditable(false, true);
|
||||||
|
|
||||||
|
ypos += myLineHeight + VGAP * 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Cartridge3EPlusWidget::loadConfig()
|
void Cartridge3EPlusWidget::loadConfig()
|
||||||
{
|
{
|
||||||
|
CartridgeEnhancedWidget::loadConfig();
|
||||||
updateUIState();
|
updateUIState();
|
||||||
CartDebugWidget::loadConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Cartridge3EPlusWidget::handleCommand(CommandSender* sender,
|
void Cartridge3EPlusWidget::handleCommand(CommandSender* sender,
|
||||||
int cmd, int data, int id)
|
int cmd, int data, int id)
|
||||||
{
|
{
|
||||||
uInt16 segment = 0;
|
const uInt16 segment = id;
|
||||||
|
|
||||||
switch(cmd)
|
switch(cmd)
|
||||||
{
|
{
|
||||||
case kBank0Changed:
|
case kBankChanged:
|
||||||
segment = 0;
|
case kRomRamChanged:
|
||||||
break;
|
{
|
||||||
case kBank1Changed:
|
const bool isROM = myBankType[segment]->getSelectedTag() == "ROM";
|
||||||
segment = 1;
|
int bank = myBankWidgets[segment]->getSelected();
|
||||||
break;
|
|
||||||
case kBank2Changed:
|
myBankCommit[segment]->setEnabled((isROM && bank < myCart.romBankCount())
|
||||||
segment = 2;
|
|| (!isROM && bank < myCart.ramBankCount()));
|
||||||
break;
|
|
||||||
case kBank3Changed:
|
|
||||||
segment = 3;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kChangeBank:
|
||||||
|
{
|
||||||
// Ignore bank if either number or type hasn't been selected
|
// Ignore bank if either number or type hasn't been selected
|
||||||
if(myBankNumber[segment]->getSelected() < 0 ||
|
if(myBankWidgets[segment]->getSelected() < 0 ||
|
||||||
myBankType[segment]->getSelected() < 0)
|
myBankType[segment]->getSelected() < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uInt8 bank = (segment << myCart.BANK_BITS) |
|
uInt8 bank = myBankWidgets[segment]->getSelected();
|
||||||
(myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK);
|
|
||||||
|
|
||||||
myCart.unlockBank();
|
myCart.unlockBank();
|
||||||
|
|
||||||
if(myBankType[segment]->getSelectedTag() == "ROM")
|
if(myBankType[segment]->getSelectedTag() == "ROM")
|
||||||
myCart.bankROM(bank);
|
myCart.bank(bank, segment);
|
||||||
else
|
else
|
||||||
myCart.bankRAM(bank);
|
myCart.bank(bank + myCart.romBankCount(), segment);
|
||||||
|
|
||||||
myCart.lockBank();
|
myCart.lockBank();
|
||||||
invalidate();
|
invalidate();
|
||||||
updateUIState();
|
updateUIState();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
break;
|
||||||
string Cartridge3EPlusWidget::bankState()
|
|
||||||
{
|
|
||||||
ostringstream& buf = buffer();
|
|
||||||
|
|
||||||
// In this scheme, consecutive 512b segments are either both ROM or both RAM;
|
|
||||||
// we only need to look at the lower segment to determine what the 1K bank is
|
|
||||||
for(int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
uInt16 bank = myCart.bankInUse[i*2];
|
|
||||||
|
|
||||||
if(bank == myCart.BANK_UNDEFINED) // never accessed
|
|
||||||
{
|
|
||||||
buf << " U!";
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
int bankno = bank & myCart.BIT_BANK_MASK;
|
|
||||||
|
|
||||||
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
|
|
||||||
buf << " RAM " << bankno;
|
|
||||||
else
|
|
||||||
buf << " ROM " << bankno;
|
|
||||||
}
|
|
||||||
if(i < 3)
|
|
||||||
buf << " /";
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Cartridge3EPlusWidget::updateUIState()
|
void Cartridge3EPlusWidget::updateUIState()
|
||||||
{
|
{
|
||||||
// Set description for each 512b bank state (@ each index)
|
// Set description for each 1K segment state (@ each index)
|
||||||
// Set contents for actual banks number and type (@ each even index)
|
// Set contents for actual banks number and type (@ each even index)
|
||||||
for(int i = 0; i < 8; ++i)
|
for(int seg = 0; seg < myCart3EP.myBankSegs; ++seg)
|
||||||
{
|
|
||||||
uInt16 bank = myCart.bankInUse[i];
|
|
||||||
|
|
||||||
if(bank == myCart.BANK_UNDEFINED) // never accessed
|
|
||||||
{
|
|
||||||
myBankState[i]->setText("Undefined");
|
|
||||||
if(i % 2 == 0)
|
|
||||||
{
|
|
||||||
myBankNumber[i/2]->clearSelection();
|
|
||||||
myBankType[i/2]->clearSelection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
uInt16 bank = myCart.getSegmentBank(seg);
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
int bankno = bank & myCart.BIT_BANK_MASK;
|
|
||||||
|
|
||||||
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
|
if(bank >= myCart.romBankCount()) // was RAM mapped here?
|
||||||
{
|
{
|
||||||
if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port
|
uInt16 ramBank = bank - myCart.romBankCount();
|
||||||
{
|
|
||||||
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
|
buf << "RAM @ $" << Common::Base::HEX4
|
||||||
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (W)";
|
<< (ramBank << myCart3EP.myBankShift) << " (R)";
|
||||||
myBankState[i]->setText(buf.str());
|
myBankState[seg * 2]->setText(buf.str());
|
||||||
|
|
||||||
|
buf.str("");
|
||||||
|
buf << "RAM @ $" << Common::Base::HEX4
|
||||||
|
<< ((ramBank << myCart3EP.myBankShift) + myCart3EP.myBankSize) << " (W)";
|
||||||
|
myBankState[seg * 2 + 1]->setText(buf.str());
|
||||||
|
|
||||||
|
myBankWidgets[seg]->setSelectedIndex(ramBank);
|
||||||
|
myBankType[seg]->setSelected("RAM");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
|
buf << "ROM @ $" << Common::Base::HEX4
|
||||||
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (R)";
|
<< ((bank << myCart3EP.myBankShift));
|
||||||
myBankState[i]->setText(buf.str());
|
myBankState[seg * 2]->setText(buf.str());
|
||||||
}
|
|
||||||
|
|
||||||
if(i % 2 == 0)
|
buf.str("");
|
||||||
{
|
buf << "ROM @ $" << Common::Base::HEX4
|
||||||
myBankNumber[i/2]->setSelected(bankno);
|
<< ((bank << myCart3EP.myBankShift) + myCart3EP.myBankSize);
|
||||||
myBankType[i/2]->setSelected("RAM");
|
myBankState[seg * 2 + 1]->setText(buf.str());
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(bank & myCart.BITMASK_LOWERUPPER) // upper is high 512b
|
|
||||||
{
|
|
||||||
buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
|
|
||||||
<< ((bankno << myCart.RAM_BANK_TO_POWER) + myCart.RAM_BANK_SIZE);
|
|
||||||
myBankState[i]->setText(buf.str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
|
|
||||||
<< (bankno << myCart.RAM_BANK_TO_POWER);
|
|
||||||
myBankState[i]->setText(buf.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i % 2 == 0)
|
myBankWidgets[seg]->setSelectedIndex(bank);
|
||||||
{
|
myBankType[seg]->setSelected("ROM");
|
||||||
myBankNumber[i/2]->setSelected(bankno);
|
|
||||||
myBankType[i/2]->setSelected("ROM");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt32 Cartridge3EPlusWidget::internalRamSize()
|
|
||||||
{
|
|
||||||
return 32*1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt32 Cartridge3EPlusWidget::internalRamRPort(int start)
|
|
||||||
{
|
|
||||||
return 0x0000 + start;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
string Cartridge3EPlusWidget::internalRamDescription()
|
string Cartridge3EPlusWidget::internalRamDescription()
|
||||||
{
|
{
|
||||||
ostringstream desc;
|
ostringstream desc;
|
||||||
|
|
||||||
desc << "Accessible 512b at a time via:\n"
|
desc << "Accessible 512b at a time via:\n"
|
||||||
<< " $f000/$f400/$f800/$fc00 for Read Access\n"
|
<< " $f000/$f400/$f800/$fc00 for read access\n"
|
||||||
<< " $f200/$f600/$fa00/$fe00 for Write Access (+$200)";
|
<< " $f200/$f600/$fa00/$fe00 for write access";
|
||||||
|
|
||||||
return desc.str();
|
return desc.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
const ByteArray& Cartridge3EPlusWidget::internalRamOld(int start, int count)
|
|
||||||
{
|
|
||||||
myRamOld.clear();
|
|
||||||
for(int i = 0; i < count; i++)
|
|
||||||
myRamOld.push_back(myOldState.internalram[start + i]);
|
|
||||||
return myRamOld;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
const ByteArray& Cartridge3EPlusWidget::internalRamCurrent(int start, int count)
|
|
||||||
{
|
|
||||||
myRamCurrent.clear();
|
|
||||||
for(int i = 0; i < count; i++)
|
|
||||||
myRamCurrent.push_back(myCart.myRAM[start + i]);
|
|
||||||
return myRamCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void Cartridge3EPlusWidget::internalRamSetValue(int addr, uInt8 value)
|
|
||||||
{
|
|
||||||
myCart.myRAM[addr] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt8 Cartridge3EPlusWidget::internalRamGetValue(int addr)
|
|
||||||
{
|
|
||||||
return myCart.myRAM[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
const std::array<Cartridge3EPlusWidget::BankID, 4> Cartridge3EPlusWidget::bankEnum = {
|
|
||||||
kBank0Changed, kBank1Changed, kBank2Changed, kBank3Changed
|
|
||||||
};
|
|
||||||
|
|
|
@ -23,9 +23,9 @@ class ButtonWidget;
|
||||||
class EditTextWidget;
|
class EditTextWidget;
|
||||||
class PopUpWidget;
|
class PopUpWidget;
|
||||||
|
|
||||||
#include "CartDebugWidget.hxx"
|
#include "CartEnhancedWidget.hxx"
|
||||||
|
|
||||||
class Cartridge3EPlusWidget : public CartDebugWidget
|
class Cartridge3EPlusWidget : public CartridgeEnhancedWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cartridge3EPlusWidget(GuiObject* boss, const GUI::Font& lfont,
|
Cartridge3EPlusWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||||
|
@ -35,46 +35,33 @@ class Cartridge3EPlusWidget : public CartDebugWidget
|
||||||
virtual ~Cartridge3EPlusWidget() = default;
|
virtual ~Cartridge3EPlusWidget() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
string manufacturer() override { return "Thomas Jentzsch"; }
|
||||||
|
|
||||||
|
string description() override;
|
||||||
|
|
||||||
|
void bankSelect(int& ypos) override;
|
||||||
|
|
||||||
|
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||||
|
|
||||||
void updateUIState();
|
void updateUIState();
|
||||||
|
|
||||||
private:
|
void loadConfig() override;
|
||||||
Cartridge3EPlus& myCart;
|
|
||||||
|
string internalRamDescription() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Cartridge3EPlus& myCart3EP;
|
||||||
|
|
||||||
std::array<PopUpWidget*, 4> myBankNumber{nullptr};
|
|
||||||
std::array<PopUpWidget*, 4> myBankType{nullptr};
|
std::array<PopUpWidget*, 4> myBankType{nullptr};
|
||||||
std::array<ButtonWidget*, 4> myBankCommit{nullptr};
|
std::array<ButtonWidget*, 4> myBankCommit{nullptr};
|
||||||
std::array<EditTextWidget*, 8> myBankState{nullptr};
|
std::array<EditTextWidget*, 8> myBankState{nullptr};
|
||||||
|
|
||||||
struct CartState {
|
enum {
|
||||||
ByteArray internalram;
|
kRomRamChanged = 'rrCh',
|
||||||
|
kChangeBank = 'chBk',
|
||||||
};
|
};
|
||||||
CartState myOldState;
|
|
||||||
|
|
||||||
enum BankID {
|
|
||||||
kBank0Changed = 'b0CH',
|
|
||||||
kBank1Changed = 'b1CH',
|
|
||||||
kBank2Changed = 'b2CH',
|
|
||||||
kBank3Changed = 'b3CH'
|
|
||||||
};
|
|
||||||
static const std::array<BankID, 4> bankEnum;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void saveOldState() override;
|
|
||||||
void loadConfig() override;
|
|
||||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
|
||||||
|
|
||||||
string bankState() override;
|
|
||||||
|
|
||||||
// start of functions for Cartridge RAM tab
|
|
||||||
uInt32 internalRamSize() override;
|
|
||||||
uInt32 internalRamRPort(int start) override;
|
|
||||||
string internalRamDescription() override;
|
|
||||||
const ByteArray& internalRamOld(int start, int count) override;
|
|
||||||
const ByteArray& internalRamCurrent(int start, int count) override;
|
|
||||||
void internalRamSetValue(int addr, uInt8 value) override;
|
|
||||||
uInt8 internalRamGetValue(int addr) override;
|
|
||||||
// end of functions for Cartridge RAM tab
|
|
||||||
|
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
Cartridge3EPlusWidget() = delete;
|
Cartridge3EPlusWidget() = delete;
|
||||||
Cartridge3EPlusWidget(const Cartridge3EPlusWidget&) = delete;
|
Cartridge3EPlusWidget(const Cartridge3EPlusWidget&) = delete;
|
||||||
|
|
|
@ -23,129 +23,134 @@
|
||||||
Cartridge3EWidget::Cartridge3EWidget(
|
Cartridge3EWidget::Cartridge3EWidget(
|
||||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||||
int x, int y, int w, int h, Cartridge3E& cart)
|
int x, int y, int w, int h, Cartridge3E& cart)
|
||||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||||
myCart(cart),
|
|
||||||
myNumRomBanks(uInt32(cart.mySize >> 11)),
|
|
||||||
myNumRamBanks(32)
|
|
||||||
{
|
{
|
||||||
size_t size = cart.mySize;
|
initialize();
|
||||||
|
|
||||||
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.startBank() < myNumRomBanks)
|
|
||||||
info << "Startup bank = " << cart.startBank() << " (ROM)\n";
|
|
||||||
else
|
|
||||||
info << "Startup bank = " << (cart.startBank()-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 = 2,
|
|
||||||
ypos = addBaseInformation(size, "TigerVision", info.str()) + myLineHeight;
|
|
||||||
|
|
||||||
VariantList romitems;
|
|
||||||
for(uInt32 i = 0; i < myNumRomBanks; ++i)
|
|
||||||
VarList::push_back(romitems, i);
|
|
||||||
VarList::push_back(romitems, "Inactive", "");
|
|
||||||
|
|
||||||
VariantList ramitems;
|
|
||||||
for(uInt32 i = 0; i < myNumRamBanks; ++i)
|
|
||||||
VarList::push_back(ramitems, i);
|
|
||||||
VarList::push_back(ramitems, "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(), TextAlign::Left);
|
|
||||||
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::saveOldState()
|
string Cartridge3EWidget::description()
|
||||||
{
|
{
|
||||||
myOldState.internalram.clear();
|
ostringstream info;
|
||||||
|
size_t size;
|
||||||
|
const ByteBuffer& image = myCart.getImage(size);
|
||||||
|
uInt16 numRomBanks = myCart.romBankCount();
|
||||||
|
uInt16 numRamBanks = myCart.ramBankCount();
|
||||||
|
|
||||||
for(uInt32 i = 0; i < internalRamSize(); ++i)
|
|
||||||
myOldState.internalram.push_back(myCart.myRAM[i]);
|
|
||||||
|
|
||||||
myOldState.bank = myCart.myCurrentBank;
|
info << "3E cartridge (3F + RAM),\n"
|
||||||
|
<< " " << numRomBanks << " 2K ROM banks, " << numRamBanks << " 1K RAM banks\n"
|
||||||
|
<< "First 2K (ROM) selected by writing to $3F\n"
|
||||||
|
"First 2K (RAM) selected by writing to $3E\n";
|
||||||
|
info << CartridgeEnhancedWidget::ramDescription();
|
||||||
|
info << "Last 2K always points to last 2K of ROM\n";
|
||||||
|
|
||||||
|
if(myCart.startBank() < numRomBanks)
|
||||||
|
info << "Startup bank = " << myCart.startBank() << " (ROM)\n";
|
||||||
|
else
|
||||||
|
info << "Startup bank = " << (myCart.startBank() - numRomBanks) << " (RAM)\n";
|
||||||
|
|
||||||
|
// Eventually, we should query this from the debugger/disassembler
|
||||||
|
uInt16 start = (image[size-3] << 8) | image[size-4];
|
||||||
|
start -= start % 0x1000;
|
||||||
|
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
|
||||||
|
|
||||||
|
return info.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void Cartridge3EWidget::bankList(uInt16 bankCount, int seg, VariantList& items, int& width)
|
||||||
|
{
|
||||||
|
CartridgeEnhancedWidget::bankList(bankCount, seg, items, width);
|
||||||
|
|
||||||
|
VarList::push_back(items, "Inactive", "");
|
||||||
|
width = _font.getStringWidth("Inactive");
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void Cartridge3EWidget::bankSelect(int& ypos)
|
||||||
|
{
|
||||||
|
int xpos = 2;
|
||||||
|
VariantList items;
|
||||||
|
int pw;
|
||||||
|
|
||||||
|
myBankWidgets = make_unique<PopUpWidget* []>(2);
|
||||||
|
|
||||||
|
bankList(myCart.romBankCount(), 0, items, pw);
|
||||||
|
myBankWidgets[0] =
|
||||||
|
new PopUpWidget(_boss, _font, xpos, ypos - 2, pw,
|
||||||
|
myLineHeight, items, "Set bank ",
|
||||||
|
_font.getStringWidth("Set bank "), kBankChanged);
|
||||||
|
myBankWidgets[0]->setTarget(this);
|
||||||
|
myBankWidgets[0]->setID(0);
|
||||||
|
addFocusWidget(myBankWidgets[0]);
|
||||||
|
|
||||||
|
StaticTextWidget* t = new StaticTextWidget(_boss, _font, myBankWidgets[0]->getRight(), ypos - 1, " (ROM)");
|
||||||
|
|
||||||
|
xpos = t->getRight() + 20;
|
||||||
|
items.clear();
|
||||||
|
bankList(myCart.ramBankCount(), 0, items, pw);
|
||||||
|
myBankWidgets[1] =
|
||||||
|
new PopUpWidget(_boss, _font, xpos, ypos - 2, pw,
|
||||||
|
myLineHeight, items, "", 0, kRAMBankChanged);
|
||||||
|
myBankWidgets[1]->setTarget(this);
|
||||||
|
myBankWidgets[1]->setID(1);
|
||||||
|
addFocusWidget(myBankWidgets[1]);
|
||||||
|
|
||||||
|
new StaticTextWidget(_boss, _font, myBankWidgets[1]->getRight(), ypos - 1, " (RAM)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Cartridge3EWidget::loadConfig()
|
void Cartridge3EWidget::loadConfig()
|
||||||
{
|
{
|
||||||
if(myCart.myCurrentBank < 256)
|
uInt16 oldBank = myOldState.banks[0];
|
||||||
|
uInt16 bank = myCart.getBank();
|
||||||
|
|
||||||
|
if(myCart.getBank() < myCart.romBankCount())
|
||||||
{
|
{
|
||||||
myROMBank->setSelectedIndex(myCart.myCurrentBank % myNumRomBanks, myOldState.bank != myCart.myCurrentBank);
|
myBankWidgets[0]->setSelectedIndex(bank, oldBank != bank);
|
||||||
myRAMBank->setSelectedMax(myOldState.bank >= 256);
|
myBankWidgets[1]->setSelectedMax(oldBank >= myCart.romBankCount());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
myROMBank->setSelectedMax(myOldState.bank < 256);
|
myBankWidgets[0]->setSelectedMax(oldBank < myCart.romBankCount());
|
||||||
myRAMBank->setSelectedIndex(myCart.myCurrentBank - 256, myOldState.bank != myCart.myCurrentBank);
|
myBankWidgets[1]->setSelectedIndex(bank - myCart.romBankCount(), oldBank != bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
CartDebugWidget::loadConfig();
|
CartDebugWidget::loadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Cartridge3EWidget::handleCommand(CommandSender* sender,
|
void Cartridge3EWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
|
||||||
int cmd, int data, int id)
|
|
||||||
{
|
{
|
||||||
uInt16 bank = 0;
|
uInt16 bank = 0;
|
||||||
|
|
||||||
if(cmd == kROMBankChanged)
|
if(cmd == kBankChanged)
|
||||||
{
|
{
|
||||||
if(myROMBank->getSelected() < int(myNumRomBanks))
|
if(myBankWidgets[0]->getSelected() < myCart.romBankCount())
|
||||||
{
|
{
|
||||||
bank = myROMBank->getSelected();
|
bank = myBankWidgets[0]->getSelected();
|
||||||
myRAMBank->setSelectedMax();
|
myBankWidgets[1]->setSelectedMax();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bank = 256; // default to first RAM bank
|
bank = myCart.romBankCount(); // default to first RAM bank
|
||||||
myRAMBank->setSelectedIndex(0);
|
myBankWidgets[1]->setSelectedIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(cmd == kRAMBankChanged)
|
else if(cmd == kRAMBankChanged)
|
||||||
{
|
{
|
||||||
if(myRAMBank->getSelected() < int(myNumRamBanks))
|
if(myBankWidgets[1]->getSelected() < myCart.ramBankCount())
|
||||||
{
|
{
|
||||||
myROMBank->setSelectedMax();
|
myBankWidgets[0]->setSelectedMax();
|
||||||
bank = myRAMBank->getSelected() + 256;
|
bank = myBankWidgets[1]->getSelected() + myCart.romBankCount();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bank = 0; // default to first ROM bank
|
bank = 0; // default to first ROM bank
|
||||||
myROMBank->setSelectedIndex(0);
|
myBankWidgets[0]->setSelectedIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myCart.unlockBank();
|
myCart.unlockBank();
|
||||||
myCart.bank(bank);
|
myCart.bank(bank);
|
||||||
myCart.lockBank();
|
myCart.lockBank();
|
||||||
|
@ -156,65 +161,13 @@ void Cartridge3EWidget::handleCommand(CommandSender* sender,
|
||||||
string Cartridge3EWidget::bankState()
|
string Cartridge3EWidget::bankState()
|
||||||
{
|
{
|
||||||
ostringstream& buf = buffer();
|
ostringstream& buf = buffer();
|
||||||
|
uInt16 bank = myCart.getBank();
|
||||||
|
|
||||||
uInt16& bank = myCart.myCurrentBank;
|
if(bank < myCart.romBankCount())
|
||||||
if(bank < 256)
|
buf << "ROM bank #" << std::dec << bank % myCart.romBankCount() << ", RAM inactive";
|
||||||
buf << "ROM bank #" << std::dec << bank % myNumRomBanks << ", RAM inactive";
|
|
||||||
else
|
else
|
||||||
buf << "ROM inactive, RAM bank #" << std::dec << bank % myNumRomBanks;
|
buf << "ROM inactive, RAM bank #"
|
||||||
|
<< std::dec << (bank - myCart.romBankCount()) % myCart.ramBankCount();
|
||||||
|
|
||||||
return buf.str();
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt32 Cartridge3EWidget::internalRamSize()
|
|
||||||
{
|
|
||||||
return 32*1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt32 Cartridge3EWidget::internalRamRPort(int start)
|
|
||||||
{
|
|
||||||
return 0x0000 + start;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
string Cartridge3EWidget::internalRamDescription()
|
|
||||||
{
|
|
||||||
ostringstream desc;
|
|
||||||
desc << "Accessible 1K at a time via:\n"
|
|
||||||
<< " $F000 - $F3FF used for Read Access\n"
|
|
||||||
<< " $F400 - $F7FF used for Write Access";
|
|
||||||
|
|
||||||
return desc.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
const ByteArray& Cartridge3EWidget::internalRamOld(int start, int count)
|
|
||||||
{
|
|
||||||
myRamOld.clear();
|
|
||||||
for(int i = 0; i < count; i++)
|
|
||||||
myRamOld.push_back(myOldState.internalram[start + i]);
|
|
||||||
return myRamOld;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
const ByteArray& Cartridge3EWidget::internalRamCurrent(int start, int count)
|
|
||||||
{
|
|
||||||
myRamCurrent.clear();
|
|
||||||
for(int i = 0; i < count; i++)
|
|
||||||
myRamCurrent.push_back(myCart.myRAM[start + i]);
|
|
||||||
return myRamCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void Cartridge3EWidget::internalRamSetValue(int addr, uInt8 value)
|
|
||||||
{
|
|
||||||
myCart.myRAM[addr] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt8 Cartridge3EWidget::internalRamGetValue(int addr)
|
|
||||||
{
|
|
||||||
return myCart.myRAM[addr];
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,11 +19,12 @@
|
||||||
#define CARTRIDGE3E_WIDGET_HXX
|
#define CARTRIDGE3E_WIDGET_HXX
|
||||||
|
|
||||||
class Cartridge3E;
|
class Cartridge3E;
|
||||||
class PopUpWidget;
|
|
||||||
|
|
||||||
#include "CartDebugWidget.hxx"
|
#include "CartEnhancedWidget.hxx"
|
||||||
|
|
||||||
class Cartridge3EWidget : public CartDebugWidget
|
// Note: This class supports 3EX too
|
||||||
|
|
||||||
|
class Cartridge3EWidget : public CartridgeEnhancedWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cartridge3EWidget(GuiObject* boss, const GUI::Font& lfont,
|
Cartridge3EWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||||
|
@ -33,39 +34,28 @@ class Cartridge3EWidget : public CartDebugWidget
|
||||||
virtual ~Cartridge3EWidget() = default;
|
virtual ~Cartridge3EWidget() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Cartridge3E& myCart;
|
|
||||||
const uInt32 myNumRomBanks{0};
|
|
||||||
const uInt32 myNumRamBanks{0};
|
|
||||||
PopUpWidget *myROMBank{nullptr}, *myRAMBank{nullptr};
|
|
||||||
|
|
||||||
struct CartState {
|
|
||||||
ByteArray internalram;
|
|
||||||
uInt16 bank;
|
|
||||||
};
|
|
||||||
CartState myOldState;
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
kROMBankChanged = 'rmCH',
|
|
||||||
kRAMBankChanged = 'raCH'
|
kRAMBankChanged = 'raCH'
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void saveOldState() override;
|
string manufacturer() override { return "Andrew Davie & Thomas Jentzsch"; }
|
||||||
|
|
||||||
|
string description() override;
|
||||||
|
|
||||||
|
void bankList(uInt16 bankCount, int seg, VariantList& items, int& width) override;
|
||||||
|
|
||||||
|
void bankSelect(int& ypos) override;
|
||||||
|
|
||||||
|
uInt16 bankSegs() override { return 1; }
|
||||||
|
|
||||||
void loadConfig() override;
|
void loadConfig() override;
|
||||||
|
|
||||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||||
|
|
||||||
string bankState() override;
|
string bankState() override;
|
||||||
|
|
||||||
// start of functions for Cartridge RAM tab
|
private:
|
||||||
uInt32 internalRamSize() override;
|
|
||||||
uInt32 internalRamRPort(int start) override;
|
|
||||||
string internalRamDescription() override;
|
|
||||||
const ByteArray& internalRamOld(int start, int count) override;
|
|
||||||
const ByteArray& internalRamCurrent(int start, int count) override;
|
|
||||||
void internalRamSetValue(int addr, uInt8 value) override;
|
|
||||||
uInt8 internalRamGetValue(int addr) override;
|
|
||||||
// end of functions for Cartridge RAM tab
|
|
||||||
|
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
Cartridge3EWidget() = delete;
|
Cartridge3EWidget() = delete;
|
||||||
Cartridge3EWidget(const Cartridge3EWidget&) = delete;
|
Cartridge3EWidget(const Cartridge3EWidget&) = delete;
|
||||||
|
|
|
@ -16,79 +16,33 @@
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include "Cart3F.hxx"
|
#include "Cart3F.hxx"
|
||||||
#include "PopUpWidget.hxx"
|
|
||||||
#include "Cart3FWidget.hxx"
|
#include "Cart3FWidget.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Cartridge3FWidget::Cartridge3FWidget(
|
Cartridge3FWidget::Cartridge3FWidget(
|
||||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||||
int x, int y, int w, int h, Cartridge3F& cart)
|
int x, int y, int w, int h, Cartridge3F& cart)
|
||||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||||
myCart(cart)
|
|
||||||
{
|
{
|
||||||
size_t size = cart.mySize;
|
myHotspotDelta = 0;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
string Cartridge3FWidget::description()
|
||||||
|
{
|
||||||
ostringstream info;
|
ostringstream info;
|
||||||
|
size_t size;
|
||||||
|
const ByteBuffer& image = myCart.getImage(size);
|
||||||
|
|
||||||
info << "Tigervision 3F cartridge, 2 - 256 2K banks\n"
|
info << "Tigervision 3F cartridge, 2 - 256 2K banks\n"
|
||||||
<< "Startup bank = " << cart.startBank() << " or undetermined\n"
|
<< "First 2K bank selected by writing to " << hotspotStr() << "\n"
|
||||||
<< "First 2K bank selected by writing to $3F\n"
|
<< "Last 2K always points to last 2K of ROM\n"
|
||||||
<< "Last 2K always points to last 2K of ROM\n";
|
<< "Startup bank = " << myCart.startBank() << " or undetermined\n";
|
||||||
|
|
||||||
// Eventually, we should query this from the debugger/disassembler
|
// Eventually, we should query this from the debugger/disassembler
|
||||||
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
|
uInt16 start = (image[size-3] << 8) | image[size-4];
|
||||||
start -= start % 0x1000;
|
start -= start % 0x1000;
|
||||||
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
|
info << "Bank RORG $" << Common::Base::HEX4 << start << "\n";
|
||||||
|
|
||||||
int xpos = 2,
|
return info.str();
|
||||||
ypos = addBaseInformation(size, "TigerVision", info.str()) + myLineHeight;
|
|
||||||
|
|
||||||
VariantList items;
|
|
||||||
for(uInt16 i = 0; i < cart.bankCount(); ++i)
|
|
||||||
VarList::push_back(items, 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()
|
|
||||||
{
|
|
||||||
Debugger& dbg = instance().debugger();
|
|
||||||
CartDebug& cart = dbg.cartDebug();
|
|
||||||
const CartState& state = static_cast<const CartState&>(cart.getState());
|
|
||||||
const CartState& oldstate = static_cast<const CartState&>(cart.getOldState());
|
|
||||||
|
|
||||||
myBank->setSelectedIndex(myCart.getBank(0), state.bank != oldstate.bank);
|
|
||||||
|
|
||||||
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 = #" << std::dec << myCart.myCurrentBank << ", hotspot = $3F";
|
|
||||||
|
|
||||||
return buf.str();
|
|
||||||
}
|
}
|
||||||
|
|