Merge branch 'master' into feature-highscores
|
@ -31,3 +31,5 @@ src/**/*.psess
|
|||
src/**/*.vspx
|
||||
src/**/**.pdb
|
||||
Stella.xcscheme
|
||||
src/tools/fonts/*
|
||||
|
||||
|
|
22
Announce.txt
|
@ -9,7 +9,7 @@
|
|||
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
|
||||
|
@ -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
|
||||
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:
|
||||
|
||||
* Binaries for Windows Vista/7/8/10 :
|
||||
Stella-6.1-win32.exe (32-bit EXE installer)
|
||||
Stella-6.1-x64.exe (64-bit EXE installer)
|
||||
Stella-6.1-windows.zip (32/64 bit versions)
|
||||
Stella-6.2.1-win32.exe (32-bit EXE installer)
|
||||
Stella-6.2.1-x64.exe (64-bit EXE installer)
|
||||
Stella-6.2.1-windows.zip (32/64 bit versions)
|
||||
|
||||
* 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 :
|
||||
stella_6.1-1_i386.deb
|
||||
stella_6.1-1_amd64.deb
|
||||
stella_6.2.1-1_i386.deb
|
||||
stella_6.2.1-1_amd64.deb
|
||||
|
||||
* Binary distribution in 32-bit & 64-bit RPM format :
|
||||
stella-6.1-2.i386.rpm
|
||||
stella-6.1-2.x86_64.rpm
|
||||
stella-6.2.1-2.i386.rpm
|
||||
stella-6.2.1-2.x86_64.rpm
|
||||
|
||||
* Source code distribution for all platforms :
|
||||
stella-6.1-src.tar.xz
|
||||
stella-6.2.1-src.tar.xz
|
||||
|
||||
|
||||
Distribution Site
|
||||
|
|
161
Changes.txt
|
@ -12,7 +12,155 @@
|
|||
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:
|
||||
- Because of major event remapping changes, all remappings will be reset
|
||||
|
@ -119,7 +267,9 @@
|
|||
|
||||
* 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.
|
||||
|
||||
* 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,
|
||||
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 libretro port, and fixed display for OpenGLES renderers.
|
||||
|
@ -192,7 +346,8 @@
|
|||
|
||||
* 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)
|
||||
|
|
7
Makefile
|
@ -8,14 +8,11 @@
|
|||
## SS SS tt ee ll ll aa aa
|
||||
## 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
|
||||
##
|
||||
## See the file "License.txt" for information on usage and redistribution of
|
||||
## 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_USE := stella-pgo$(EXEEXT)
|
||||
|
||||
PROFILE_DIR = $(CURDIR)/profile
|
||||
PROFILE_DIR = $(CURDIR)/test/roms/profile
|
||||
PROFILE_OUT = $(PROFILE_DIR)/out
|
||||
PROFILE_STAMP = profile.stamp
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@ environment:
|
|||
|
||||
Configuration: Release
|
||||
|
||||
SDL2_version: 2.0.10
|
||||
SDL2_version: 2.0.12
|
||||
|
||||
|
||||
install:
|
||||
- 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:\"
|
||||
xcopy /S "C:\SDL2-%SDL2_version%\include" src\common
|
||||
|
||||
|
|
|
@ -458,11 +458,17 @@ elif test "$have_gcc" = yes; then
|
|||
fi
|
||||
|
||||
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_minor=`echo $cxx_version | cut -d '.' -f 2`
|
||||
cxx_version="$cxx_version, ok"
|
||||
cxx_verc_fail=no
|
||||
# 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_verc_fail=no
|
||||
else
|
||||
cxx_version="$cxx_version, bad"
|
||||
cxx_verc_fail=yes
|
||||
fi
|
||||
;;
|
||||
'not found')
|
||||
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
|
||||
|
||||
* Version 6.0.2 release
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<body>
|
||||
|
||||
<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><h4><b>(a work in progress)</b></h4></center>
|
||||
<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
|
||||
override automatic code/data determination in the disassembly. For now,
|
||||
the following directives are supported: CODE, GFX, PGFX, DATA, ROW.
|
||||
These directives can be entered at the debugger prompt, or (automatically)
|
||||
the following directives are supported: CODE, GFX, PGFX, COL, PCOL, BCOL, AUD, DATA, ROW.
|
||||
These directives can be entered at the debugger prompt, or be (automatically)
|
||||
loaded and saved in configuration files.</li>
|
||||
|
||||
<li>Extensive disassembly support, both from the emulation core and with help
|
||||
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
|
||||
differentiated between actual code, and 'tentative' code (ie, areas that may
|
||||
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
|
||||
<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>
|
||||
|
@ -381,12 +382,12 @@ size can be configured e.g. in the
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Backquote (`)</td>
|
||||
<td>Exit</td>
|
||||
<td>Run</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
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>
|
||||
<p><img src="graphics/debugger_options.png"></p>
|
||||
</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,
|
||||
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
|
||||
debugger with the Program Counter pointing to the instruction
|
||||
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> _icycles</td><td> Number of cycles of last instruction</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> _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>
|
||||
|
@ -906,7 +908,9 @@ Type "help 'cmd'" to see extended information about the given command.</p>
|
|||
|
||||
<pre>
|
||||
a - Set Accumulator to <value>
|
||||
aud - Mark 'AUD' range in disassembly
|
||||
base - Set default number base to <base> (bin, dec, hex)
|
||||
bcol - Mark 'BCOL' range in disassembly
|
||||
break - Set/clear breakpoint at <address> and <bank>
|
||||
breakif - Set/clear breakpoint on <condition>
|
||||
breaklabel - Set/clear breakpoint on <address> (no mirrors, all banks)
|
||||
|
@ -919,6 +923,7 @@ clearsavestateifs - Clear all savestate points
|
|||
clearwatches - Clear all watches
|
||||
cls - Clear prompt area of text
|
||||
code - Mark 'CODE' range in disassembly
|
||||
col - Mark 'COL' range in disassembly
|
||||
colortest - Show value xx as TIA color
|
||||
d - Decimal Mode Flag: set (0 or 1), or toggle (no arg)
|
||||
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)
|
||||
palette - Show current TIA palette
|
||||
pc - Set Program Counter to address xx
|
||||
pcol - Mark 'PCOL' range in disassembly
|
||||
pgfx - Mark 'PGFX' range in disassembly
|
||||
print - Evaluate/print expression xx in hex/dec/binary
|
||||
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
|
||||
s - Set Stack Pointer to value 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)
|
||||
savedis - Save Distella disassembly (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
|
||||
the GRP0/1 and playfield registers (remember to double-click).</p>
|
||||
|
||||
<p>The group of buttons labelled "Strobes" allows you to write to any
|
||||
of the strobe registers at any time.</p>
|
||||
<p>The buttons allow you to write to any of the strobe registers at
|
||||
any time.</p>
|
||||
|
||||
<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
|
||||
|
@ -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>
|
||||
|
||||
<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,
|
||||
as illustrated:</p>
|
||||
|
@ -1193,6 +1200,8 @@ the reason will be shown as follows:
|
|||
<li>"WTrap:" for write traps</li>
|
||||
<li>"RTrapIf:" for conditional read 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>
|
||||
</p>
|
||||
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>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.
|
||||
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
|
||||
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>
|
||||
|
@ -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
|
||||
first glance. The 'Src Addr' area shows the actual resulting operand/address
|
||||
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
|
||||
assembly, it's pretty self-explanatory. If you don't, well, you should
|
||||
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
|
||||
decreasing hierarchy:</p>
|
||||
<table border="1" cellpadding=4>
|
||||
<tr><td><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
|
||||
can be edited in either hex or binary. The bitmap is shown as large blocks.</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
|
||||
can be edited in either hex or binary. The bitmap is shown as small dashes.</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>
|
||||
<tr>
|
||||
<td>
|
||||
<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
|
||||
can be edited in either hex or binary. The bitmap is shown as large blocks.
|
||||
</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
|
||||
can be edited in either hex or binary. The bitmap is shown as small dashes.
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td>
|
||||
<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>
|
||||
|
||||
|
||||
<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
|
||||
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>
|
||||
<ul>
|
||||
<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
|
||||
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
|
||||
disassembled, regardless of whether anything has changed.</li>
|
||||
<li><b>Disassemble @ current line</b>: Disassemble from the disassembly line where the mouse was clicked.
|
||||
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>
|
||||
|
||||
|
@ -1445,7 +1513,8 @@ isn't already a defined label).</li>
|
|||
in either binary or hexidecimal.</li>
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -1520,12 +1589,12 @@ the RAM in the DPC scheme is not viewable by the 6507, so its addresses start fr
|
|||
<br>
|
||||
<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:
|
||||
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
|
||||
several options in this case:</p>
|
||||
<ol>
|
||||
<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
|
||||
will remove that directive from the range.</li>
|
||||
<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
|
||||
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
|
||||
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
|
||||
|
@ -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
|
||||
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:
|
||||
"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,
|
||||
|
|
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 |
1112
docs/index.html
|
@ -58,7 +58,7 @@
|
|||
|
||||
<center><h1>Stella for RetroN 77</h1></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>
|
||||
<br/>
|
||||
|
||||
|
|
|
@ -35,46 +35,49 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
|
|||
const GUI::Font& font)
|
||||
: Dialog(osystem, parent, font, "Cheat codes")
|
||||
{
|
||||
const int lineHeight = font.getLineHeight(),
|
||||
fontWidth = font.getMaxCharWidth(),
|
||||
buttonWidth = font.getStringWidth("Defaults") + 20,
|
||||
buttonHeight = font.getLineHeight() + 4;
|
||||
const int HBORDER = 10;
|
||||
const int VBORDER = 10 + _th;
|
||||
const int lineHeight = font.getLineHeight(),
|
||||
fontHeight = font.getFontHeight(),
|
||||
fontWidth = font.getMaxCharWidth(),
|
||||
buttonWidth = font.getStringWidth("One shot ") + fontWidth * 2.5,
|
||||
buttonHeight = font.getLineHeight() * 1.25;
|
||||
const int VBORDER = fontHeight / 2;
|
||||
const int HBORDER = fontWidth * 1.25;
|
||||
const int VGAP = fontHeight / 4;
|
||||
|
||||
int xpos, ypos;
|
||||
WidgetArray wid;
|
||||
ButtonWidget* b;
|
||||
|
||||
// Set real dimensions
|
||||
_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
|
||||
xpos = HBORDER; ypos = VBORDER;
|
||||
xpos = HBORDER; ypos = _th + VBORDER;
|
||||
myCheatList =
|
||||
new CheckListWidget(this, font, xpos, ypos, _w - buttonWidth - HBORDER * 2 - 8,
|
||||
_h - 2*buttonHeight - VBORDER);
|
||||
new CheckListWidget(this, font, xpos, ypos, _w - buttonWidth - HBORDER * 2 - fontWidth,
|
||||
_h - _th - buttonHeight - VBORDER * 3);
|
||||
myCheatList->setEditable(false);
|
||||
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,
|
||||
"Add" + ELLIPSIS, kAddCheatCmd);
|
||||
wid.push_back(b);
|
||||
ypos += lineHeight + 8;
|
||||
ypos += lineHeight + VGAP * 2;
|
||||
|
||||
myEditButton =
|
||||
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
"Edit" + ELLIPSIS, kEditCheatCmd);
|
||||
wid.push_back(myEditButton);
|
||||
ypos += lineHeight + 8;
|
||||
ypos += lineHeight + VGAP * 2;
|
||||
|
||||
myRemoveButton =
|
||||
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
"Remove", kRemCheatCmd);
|
||||
wid.push_back(myRemoveButton);
|
||||
ypos += lineHeight + 8 * 3;
|
||||
ypos += lineHeight + VGAP * 2 * 3;
|
||||
|
||||
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
"One shot" + ELLIPSIS, kAddOneShotCmd);
|
||||
|
|
|
@ -213,10 +213,9 @@ void CheatManager::enable(const string& code, bool enable)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CheatManager::loadCheatDatabase()
|
||||
{
|
||||
const string& cheatfile = myOSystem.cheatFile();
|
||||
ifstream in(cheatfile);
|
||||
if(!in)
|
||||
return;
|
||||
stringstream in;
|
||||
try { myOSystem.cheatFile().read(in); }
|
||||
catch(...) { return; }
|
||||
|
||||
string line, md5, cheat;
|
||||
string::size_type one, two, three, four;
|
||||
|
@ -253,13 +252,12 @@ void CheatManager::saveCheatDatabase()
|
|||
if(!myListIsDirty)
|
||||
return;
|
||||
|
||||
const string& cheatfile = myOSystem.cheatFile();
|
||||
ofstream out(cheatfile);
|
||||
if(!out)
|
||||
return;
|
||||
|
||||
stringstream out;
|
||||
for(const auto& iter: myCheatMap)
|
||||
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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 AudioSettings::device() const
|
||||
{
|
||||
return mySettings.getInt(SETTING_DEVICE);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool AudioSettings::enabled() const
|
||||
{
|
||||
|
@ -285,6 +291,14 @@ void AudioSettings::setVolume(uInt32 volume)
|
|||
normalize(mySettings);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void AudioSettings::setDevice(uInt32 device)
|
||||
{
|
||||
if(!myIsPersistent) return;
|
||||
|
||||
mySettings.setValue(SETTING_DEVICE, device);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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_STEREO = "audio.stereo";
|
||||
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_DPC_PITCH = "audio.dpc_pitch";
|
||||
|
||||
|
@ -59,6 +60,7 @@ class AudioSettings
|
|||
static constexpr ResamplingQuality DEFAULT_RESAMPLING_QUALITY = ResamplingQuality::lanczos_2;
|
||||
static constexpr bool DEFAULT_STEREO = false;
|
||||
static constexpr uInt32 DEFAULT_VOLUME = 80;
|
||||
static constexpr uInt32 DEFAULT_DEVICE = 0;
|
||||
static constexpr bool DEFAULT_ENABLED = true;
|
||||
static constexpr uInt32 DEFAULT_DPC_PITCH = 20000;
|
||||
|
||||
|
@ -87,6 +89,8 @@ class AudioSettings
|
|||
|
||||
uInt32 volume() const;
|
||||
|
||||
uInt32 device() const;
|
||||
|
||||
bool enabled() const;
|
||||
|
||||
uInt32 dpcPitch() const;
|
||||
|
@ -109,6 +113,8 @@ class AudioSettings
|
|||
|
||||
void setVolume(uInt32 volume);
|
||||
|
||||
void setDevice(uInt32 device);
|
||||
|
||||
void setEnabled(bool isEnabled);
|
||||
|
||||
void setPersistent(bool isPersistent);
|
||||
|
|
|
@ -126,6 +126,21 @@ void FilesystemNodeZIP::setFlags(const string& zipfile,
|
|||
_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
|
||||
{
|
||||
|
@ -181,7 +196,34 @@ size_t FilesystemNodeZIP::read(ByteBuffer& image) const
|
|||
while(myZipHandler->hasNext() && !found)
|
||||
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);
|
||||
|
||||
bool exists() const override { return _realNode && _realNode->exists(); }
|
||||
bool exists() const override;
|
||||
const string& getName() const override { return _name; }
|
||||
void setName(const string& name) override { _name = name; }
|
||||
const string& getPath() const override { return _path; }
|
||||
|
@ -63,6 +63,9 @@ class FilesystemNodeZIP : public AbstractFSNode
|
|||
AbstractFSNodePtr getParent() 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:
|
||||
FilesystemNodeZIP(const string& zipfile, const string& virtualpath,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "SDL_lib.hxx"
|
||||
#include "bspf.hxx"
|
||||
#include "Logger.hxx"
|
||||
|
@ -48,8 +50,6 @@ FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem)
|
|||
// since the structure may be needed before any FBSurface's have
|
||||
// been created
|
||||
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);
|
||||
ostringstream s;
|
||||
|
||||
s << "Supported video modes for display " << i << ":";
|
||||
Logger::debug(s.str());
|
||||
s << "Supported video modes (" << numModes << ") for display " << i << ":";
|
||||
|
||||
string lastRes = "";
|
||||
|
||||
for (int m = 0; m < numModes; m++)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
ostringstream res;
|
||||
|
||||
SDL_GetDisplayMode(i, m, &mode);
|
||||
s.str("");
|
||||
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)
|
||||
s << " (active)";
|
||||
Logger::debug(s.str());
|
||||
res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
|
||||
|
||||
if(lastRes != res.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
|
||||
|
@ -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;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -213,31 +233,13 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
|||
if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
|
||||
return false;
|
||||
|
||||
Int32 displayIndex = mode.fsIndex;
|
||||
if (displayIndex == -1)
|
||||
{
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
const bool fullScreen = mode.fsIndex != -1;
|
||||
bool forceCreateRenderer = false;
|
||||
|
||||
// save and get last windowed window's position
|
||||
updateWindowedPos();
|
||||
|
||||
// Always recreate renderer (some systems need this)
|
||||
if(myRenderer)
|
||||
{
|
||||
SDL_DestroyRenderer(myRenderer);
|
||||
myRenderer = nullptr;
|
||||
}
|
||||
// Get windowed window's last display
|
||||
Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey()));
|
||||
// Get windowed window's last position
|
||||
myWindowedPos = myOSystem.settings().getPoint(getPositionKey());
|
||||
|
||||
int posX, posY;
|
||||
|
||||
|
@ -249,7 +251,7 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
|||
posX = myWindowedPos.x;
|
||||
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;
|
||||
|
||||
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);
|
||||
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
|
||||
// keep the same handle
|
||||
// Problem is, doing so on other platforms results in flickering when
|
||||
// toggling fullscreen windowed mode
|
||||
// So we have a special case for macOS
|
||||
#ifndef BSPF_MACOS
|
||||
// Don't re-create the window if its size hasn't changed, as it's not
|
||||
// necessary, and causes flashing in fullscreen mode
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
SDL_DisplayMode adaptedSdlMode;
|
||||
const bool shouldAdapt = fullScreen && myOSystem.settings().getBool("tia.fs_refresh")
|
||||
&& gameRefreshRate()
|
||||
// take care of 59.94 Hz
|
||||
&& refreshRate() % gameRefreshRate() != 0 && refreshRate() % (gameRefreshRate() - 1) != 0;
|
||||
const bool adaptRefresh = shouldAdapt && adaptRefreshRate(displayIndex, adaptedSdlMode);
|
||||
#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)
|
||||
{
|
||||
const int d = SDL_GetWindowDisplayIndex(myWindow);
|
||||
int 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);
|
||||
myWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(myWindow)
|
||||
{
|
||||
// Even though window size stayed the same, the title may have changed
|
||||
SDL_SetWindowTitle(myWindow, title.c_str());
|
||||
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
|
||||
{
|
||||
forceCreateRenderer = true;
|
||||
myWindow = SDL_CreateWindow(title.c_str(), posX, posY,
|
||||
mode.screen.w, mode.screen.h, flags);
|
||||
if(myWindow == nullptr)
|
||||
|
@ -316,30 +316,133 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
|||
Logger::error(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
setWindowIcon();
|
||||
}
|
||||
|
||||
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
|
||||
if(video != "")
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
|
||||
|
||||
myRenderer = SDL_CreateRenderer(myWindow, -1, renderFlags);
|
||||
|
||||
detectFeatures();
|
||||
determineDimensions();
|
||||
|
||||
if(myRenderer == nullptr)
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
if(adaptRefresh)
|
||||
{
|
||||
string msg = "ERROR: Unable to create SDL renderer: " + string(SDL_GetError());
|
||||
Logger::error(msg);
|
||||
// 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;
|
||||
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 != "")
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
|
||||
|
||||
myRenderer = SDL_CreateRenderer(myWindow, -1, renderFlags);
|
||||
|
||||
detectFeatures();
|
||||
determineDimensions();
|
||||
|
||||
if(myRenderer == nullptr)
|
||||
{
|
||||
string msg = "ERROR: Unable to create SDL renderer: " + string(SDL_GetError());
|
||||
Logger::error(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
clear();
|
||||
|
||||
SDL_RendererInfo renderinfo;
|
||||
|
||||
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
|
||||
myOSystem.settings().setValue("video", renderinfo.name);
|
||||
|
||||
|
@ -407,6 +510,36 @@ bool FrameBufferSDL2::fullScreen() const
|
|||
#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()
|
||||
{
|
||||
|
@ -419,10 +552,9 @@ void FrameBufferSDL2::renderToScreen()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FrameBufferSDL2::setWindowIcon()
|
||||
{
|
||||
ASSERT_MAIN_THREAD;
|
||||
|
||||
#if !defined(BSPF_MACOS) && !defined(RETRON77)
|
||||
#include "stella_icon.hxx"
|
||||
ASSERT_MAIN_THREAD;
|
||||
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32,
|
||||
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;
|
||||
|
||||
/**
|
||||
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
|
||||
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
|
||||
window is displayed
|
||||
*/
|
||||
Int32 getCurrentDisplayIndex() override;
|
||||
|
||||
/**
|
||||
This method is called to preserve the last current windowed position.
|
||||
*/
|
||||
void updateWindowedPos() override;
|
||||
Int32 getCurrentDisplayIndex() const override;
|
||||
|
||||
/**
|
||||
Clear the frame buffer.
|
||||
|
@ -137,12 +147,12 @@ class FrameBufferSDL2 : public FrameBuffer
|
|||
/**
|
||||
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
|
||||
*/
|
||||
int scaleY(int y) const { return (y * myRenderH) / myWindowH; }
|
||||
int scaleY(int y) const override { return (y * myRenderH) / myWindowH; }
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -171,6 +181,25 @@ class FrameBufferSDL2 : public FrameBuffer
|
|||
*/
|
||||
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.
|
||||
|
||||
|
@ -223,6 +252,16 @@ class FrameBufferSDL2 : public FrameBuffer
|
|||
*/
|
||||
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:
|
||||
// The SDL video buffer
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
for (auto item : myMap)
|
||||
for (auto item : sortedMap)
|
||||
{
|
||||
if (item.first.mode == mode)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define CONTROLLERMAP_HXX
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Event.hxx"
|
||||
#include "EventHandlerConstants.hxx"
|
||||
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
//============================================================================
|
||||
|
||||
#include "KeyMap.hxx"
|
||||
#include <map>
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void KeyMap::add(const Event::Type event, const Mapping& mapping)
|
||||
{
|
||||
myMap[convertMod(mapping)] = event;
|
||||
myMap[convertMod(mapping)] = event;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -32,7 +33,7 @@ void KeyMap::add(const Event::Type event, const EventMode mode, const int key, c
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void KeyMap::erase(const Mapping& mapping)
|
||||
{
|
||||
myMap.erase(convertMod(mapping));
|
||||
myMap.erase(convertMod(mapping));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -170,9 +171,26 @@ KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const Even
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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;
|
||||
|
||||
for (auto item : myMap)
|
||||
for (auto item : sortedMap)
|
||||
{
|
||||
if (item.first.mode == mode)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <unordered_map>
|
||||
#include "Event.hxx"
|
||||
#include "EventHandlerConstants.hxx"
|
||||
#include "StellaKeys.hxx"
|
||||
|
||||
/**
|
||||
This class handles keyboard mappings in Stella.
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
*/
|
||||
namespace Common {
|
||||
|
||||
template <class T, uInt32 CAPACITY = 100>
|
||||
template <typename T, uInt32 CAPACITY = 100>
|
||||
class LinkedObjectPool
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -124,7 +124,7 @@ MouseControl::MouseControl(Console& console, const string& mode)
|
|||
int m_range = 100;
|
||||
if(!(m_axis >> m_range))
|
||||
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(myModeList.size() == 0)
|
||||
|
|
|
@ -250,9 +250,9 @@ void PhysicalJoystickHandler::setDefaultAction(int stick,
|
|||
|
||||
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
|
||||
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))
|
||||
{
|
||||
if (map.hat == JOY_CTRL_NONE)
|
||||
|
@ -687,27 +687,32 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
|
|||
}
|
||||
j->axisLastValue[axis] = value;
|
||||
}
|
||||
#ifdef GUI_SUPPORT
|
||||
else if(myHandler.hasOverlay())
|
||||
{
|
||||
// First, clamp the values to simulate digital input
|
||||
// (the only thing that the underlying code understands)
|
||||
if(value > Joystick::deadzone())
|
||||
value = 32000;
|
||||
else if(value < -Joystick::deadzone())
|
||||
value = -32000;
|
||||
else
|
||||
value = 0;
|
||||
|
||||
// Now filter out consecutive, similar values
|
||||
// (only pass on the event if the state has changed)
|
||||
if(value != j->axisLastValue[axis])
|
||||
// A value change lower than Joystick::deadzone indicates analog input which is ignored
|
||||
if((abs(j->axisLastValue[axis] - value) > Joystick::deadzone()))
|
||||
{
|
||||
#ifdef GUI_SUPPORT
|
||||
myHandler.overlay().handleJoyAxisEvent(stick, JoyAxis(axis), convertAxisValue(value), button);
|
||||
#endif
|
||||
j->axisLastValue[axis] = value;
|
||||
// First, clamp the values to simulate digital input
|
||||
// (the only thing that the underlying code understands)
|
||||
if(value > Joystick::deadzone())
|
||||
value = 32000;
|
||||
else if(value < -Joystick::deadzone())
|
||||
value = -32000;
|
||||
else
|
||||
value = 0;
|
||||
|
||||
// Now filter out consecutive, similar values
|
||||
// (only pass on the event if the state has changed)
|
||||
if(value != j->axisLastValue[axis])
|
||||
{
|
||||
myHandler.overlay().handleJoyAxisEvent(stick, JoyAxis(axis), convertAxisValue(value), button);
|
||||
|
||||
}
|
||||
}
|
||||
j->axisLastValue[axis] = value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,7 @@
|
|||
|
||||
#include "OSystem.hxx"
|
||||
#include "Console.hxx"
|
||||
#include "Settings.hxx"
|
||||
#include "EventHandler.hxx"
|
||||
#include "Sound.hxx"
|
||||
#include "StateManager.hxx"
|
||||
#include "StellaKeys.hxx"
|
||||
#include "TIASurface.hxx"
|
||||
#include "PNGLibrary.hxx"
|
||||
#include "PKeyboardHandler.hxx"
|
||||
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
|
@ -45,6 +39,7 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler&
|
|||
myHandler(handler)
|
||||
{
|
||||
Int32 version = myOSystem.settings().getInt("event_ver");
|
||||
bool updateDefaults = false;
|
||||
|
||||
// Compare if event list version has changed so that key maps became invalid
|
||||
if (version == Event::VERSION)
|
||||
|
@ -59,11 +54,37 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler&
|
|||
myKeyMap.loadMapping(list, EventMode::kKeypadMode);
|
||||
list = myOSystem.settings().getString("keymap_ui");
|
||||
myKeyMap.loadMapping(list, EventMode::kMenuMode);
|
||||
updateDefaults = true;
|
||||
}
|
||||
myKeyMap.enableMod() = myOSystem.settings().getBool("modcombo");
|
||||
|
||||
setDefaultMapping(Event::NoType, EventMode::kEmulationMode, true);
|
||||
setDefaultMapping(Event::NoType, EventMode::kMenuMode, true);
|
||||
setDefaultMapping(Event::NoType, EventMode::kEmulationMode, updateDefaults);
|
||||
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 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
|
||||
if (myKeyMap.getEventMapping(map.event, mode).size() == 0 ||
|
||||
!myKeyMap.check(mode, map.key, map.mod))
|
||||
if (myKeyMap.getEventMapping(map.event, mode).size() == 0 &&
|
||||
!isMappingUsed(mode, map))
|
||||
{
|
||||
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::PAUSE:
|
||||
case EventHandlerState::PLAYBACK:
|
||||
myHandler.handleEvent(myKeyMap.get(EventMode::kEmulationMode, key, mod), pressed, repeated);
|
||||
break;
|
||||
|
||||
|
@ -424,7 +446,11 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
|||
{Event::LoadState, KBDK_F11},
|
||||
{Event::LoadAllStates, KBDK_F11, MOD3},
|
||||
{Event::TakeSnapshot, KBDK_F12},
|
||||
#ifdef BSPF_MACOS
|
||||
{Event::TogglePauseMode, KBDK_P, KBDM_SHIFT | MOD3},
|
||||
#else
|
||||
{Event::TogglePauseMode, KBDK_PAUSE},
|
||||
#endif
|
||||
{Event::OptionsMenuMode, KBDK_TAB},
|
||||
{Event::CmdMenuMode, KBDK_BACKSLASH},
|
||||
{Event::TimeMachineMode, KBDK_T, KBDM_SHIFT},
|
||||
|
@ -436,45 +462,83 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
|||
{Event::Quit, KBDK_Q, KBDM_CTRL},
|
||||
#endif
|
||||
{Event::ReloadConsole, KBDK_R, KBDM_CTRL},
|
||||
{Event::PreviousMultiCartRom, KBDK_R, KBDM_SHIFT | KBDM_CTRL},
|
||||
|
||||
{Event::VidmodeDecrease, KBDK_MINUS, MOD3},
|
||||
{Event::VidmodeIncrease, KBDK_EQUALS, MOD3},
|
||||
{Event::VCenterDecrease, KBDK_PAGEUP, MOD3},
|
||||
{Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3},
|
||||
{Event::ScanlineAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3},
|
||||
{Event::ScanlineAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3},
|
||||
{Event::VSizeAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3},
|
||||
{Event::VSizeAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3},
|
||||
{Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3},
|
||||
{Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3},
|
||||
{Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL},
|
||||
|
||||
{Event::ToggleFullScreen, KBDK_RETURN, MOD3},
|
||||
{Event::ToggleAdaptRefresh, KBDK_R, MOD3},
|
||||
{Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT},
|
||||
{Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT},
|
||||
{Event::VidmodeStd, KBDK_1, MOD3},
|
||||
{Event::VidmodeRGB, KBDK_2, MOD3},
|
||||
{Event::VidmodeSVideo, KBDK_3, MOD3},
|
||||
{Event::VidModeComposite, KBDK_4, MOD3},
|
||||
{Event::VidModeBad, KBDK_5, MOD3},
|
||||
{Event::VidModeCustom, KBDK_6, MOD3},
|
||||
{Event::PreviousAttribute, KBDK_7, KBDM_SHIFT | MOD3},
|
||||
{Event::NextAttribute, KBDK_7, MOD3},
|
||||
{Event::DecreaseAttribute, KBDK_8, KBDM_SHIFT | MOD3},
|
||||
{Event::IncreaseAttribute, KBDK_8, MOD3},
|
||||
{Event::PhosphorDecrease, KBDK_9, KBDM_SHIFT | MOD3},
|
||||
{Event::PhosphorIncrease, KBDK_9, MOD3},
|
||||
//{Event::VidmodeStd, KBDK_1, MOD3},
|
||||
//{Event::VidmodeRGB, KBDK_2, MOD3},
|
||||
//{Event::VidmodeSVideo, KBDK_3, MOD3},
|
||||
//{Event::VidModeComposite, KBDK_4, MOD3},
|
||||
//{Event::VidModeBad, KBDK_5, MOD3},
|
||||
//{Event::VidModeCustom, KBDK_6, MOD3},
|
||||
{Event::PreviousVideoMode, KBDK_1, KBDM_SHIFT | MOD3},
|
||||
{Event::NextVideoMode, KBDK_1, MOD3},
|
||||
{Event::PreviousAttribute, KBDK_2, KBDM_SHIFT | MOD3},
|
||||
{Event::NextAttribute, KBDK_2, MOD3},
|
||||
{Event::DecreaseAttribute, KBDK_3, KBDM_SHIFT | MOD3},
|
||||
{Event::IncreaseAttribute, KBDK_3, MOD3},
|
||||
{Event::PhosphorDecrease, KBDK_4, KBDM_SHIFT | MOD3},
|
||||
{Event::PhosphorIncrease, KBDK_4, MOD3},
|
||||
{Event::TogglePhosphor, KBDK_P, MOD3},
|
||||
{Event::ScanlinesDecrease, KBDK_0, KBDM_SHIFT | MOD3},
|
||||
{Event::ScanlinesIncrease, KBDK_0, MOD3},
|
||||
{Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | 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::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::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::ToggleFrameStats, KBDK_L, MOD3},
|
||||
{Event::ToggleTimeMachine, KBDK_T, MOD3},
|
||||
|
||||
#ifdef PNG_SUPPORT
|
||||
{Event::ToggleContSnapshots, KBDK_S, MOD3},
|
||||
{Event::ToggleContSnapshotsFrame, KBDK_S, KBDM_SHIFT | MOD3},
|
||||
#endif
|
||||
|
||||
{Event::DecreaseAutoFire, KBDK_A, KBDM_SHIFT | KBDM_CTRL},
|
||||
{Event::IncreaseAutoFire, KBDK_A, KBDM_CTRL },
|
||||
{Event::HandleMouseControl, KBDK_0, KBDM_CTRL},
|
||||
{Event::ToggleGrabMouse, KBDK_G, KBDM_CTRL},
|
||||
{Event::ToggleSAPortOrder, KBDK_1, KBDM_CTRL},
|
||||
|
@ -506,6 +570,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
|||
{Event::Unwind10Menu, KBDK_RIGHT, KBDM_SHIFT | MOD3},
|
||||
{Event::UnwindAllMenu, KBDK_UP, MOD3},
|
||||
{Event::HighScoresMenuMode, KBDK_INSERT},
|
||||
{Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT},
|
||||
|
||||
#if defined(RETRON77)
|
||||
{Event::ConsoleColorToggle, KBDK_F4}, // back ("COLOR","B/W")
|
||||
|
|
|
@ -87,6 +87,9 @@ class PhysicalKeyboardHandler
|
|||
};
|
||||
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,
|
||||
EventMode mode = EventMode::kEmulationMode, bool updateDefaults = false);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "TIASurface.hxx"
|
||||
#include "Version.hxx"
|
||||
#include "PNGLibrary.hxx"
|
||||
#include "Rect.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
PNGLibrary::PNGLibrary(OSystem& osystem)
|
||||
|
@ -51,7 +52,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
|||
throw runtime_error(s);
|
||||
};
|
||||
|
||||
ifstream in(filename, std::ios_base::binary);
|
||||
std::ifstream in(filename, std::ios_base::binary);
|
||||
if(!in.is_open())
|
||||
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)
|
||||
{
|
||||
loadImageERROR("Greyscale PNG images not supported");
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
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)
|
||||
{
|
||||
ofstream out(filename, std::ios_base::binary);
|
||||
std::ofstream out(filename, std::ios_base::binary);
|
||||
if(!out.is_open())
|
||||
throw runtime_error("ERROR: Couldn't create snapshot file");
|
||||
|
||||
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();
|
||||
|
||||
// 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,
|
||||
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())
|
||||
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_structp png_ptr = nullptr;
|
||||
|
@ -290,7 +297,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
|
|||
// Figure out the correct snapshot name
|
||||
string filename;
|
||||
bool showmessage = number == 0;
|
||||
string sspath = myOSystem.snapshotSaveDir() +
|
||||
string sspath = myOSystem.snapshotSaveDir().getPath() +
|
||||
(myOSystem.settings().getString("snapname") != "int" ?
|
||||
myOSystem.romFile().getNameWithExt("")
|
||||
: 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)
|
||||
{
|
||||
(static_cast<ifstream*>(png_get_io_ptr(ctx)))->read(
|
||||
(static_cast<std::ifstream*>(png_get_io_ptr(ctx)))->read(
|
||||
reinterpret_cast<char *>(area), 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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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 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,
|
||||
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() {}
|
||||
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(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()); }
|
||||
|
||||
uInt32 x() const { return left; }
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <cmath>
|
||||
|
||||
#include "OSystem.hxx"
|
||||
#include "Console.hxx"
|
||||
#include "Serializer.hxx"
|
||||
#include "StateManager.hxx"
|
||||
#include "TIA.hxx"
|
||||
|
@ -180,7 +181,8 @@ uInt32 RewindManager::rewindStates(uInt32 numStates)
|
|||
else
|
||||
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);
|
||||
return i;
|
||||
}
|
||||
|
@ -214,7 +216,8 @@ uInt32 RewindManager::unwindStates(uInt32 numStates)
|
|||
else
|
||||
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);
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
#ifndef SOUND_NULL_HXX
|
||||
#define SOUND_NULL_HXX
|
||||
|
||||
class OSystem;
|
||||
class AudioQueue;
|
||||
class EmulationTiming;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Logger.hxx"
|
||||
#include "Sound.hxx"
|
||||
#include "OSystem.hxx"
|
||||
#include "AudioQueue.hxx"
|
||||
#include "EmulationTiming.hxx"
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
@param direction Increase or decrease the current volume by a predefined
|
||||
amount based on the direction (1 = increase, -1 =decrease)
|
||||
@param direction +1 indicates increase, -1 indicates 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.
|
||||
|
|
|
@ -56,6 +56,8 @@ SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
|
|||
return;
|
||||
}
|
||||
|
||||
queryHardware(myDevices);
|
||||
|
||||
SDL_zero(myHardwareSpec);
|
||||
if(!openDevice())
|
||||
return;
|
||||
|
@ -76,6 +78,29 @@ SoundSDL2::~SoundSDL2()
|
|||
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()
|
||||
{
|
||||
|
@ -91,7 +116,11 @@ bool SoundSDL2::openDevice()
|
|||
|
||||
if(myIsInitializedFlag)
|
||||
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);
|
||||
|
||||
if(myDevice == 0)
|
||||
|
@ -126,7 +155,8 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
|
|||
// Do we need to re-open the sound device?
|
||||
// Only do this when absolutely necessary
|
||||
if(myAudioSettings.sampleRate() != uInt32(myHardwareSpec.freq) ||
|
||||
myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples))
|
||||
myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples) ||
|
||||
myAudioSettings.device() != myDeviceId)
|
||||
openDevice();
|
||||
|
||||
myEmulationTiming = emulationTiming;
|
||||
|
@ -186,16 +216,31 @@ bool SoundSDL2::mute(bool state)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool SoundSDL2::toggleMute()
|
||||
{
|
||||
bool enabled = myAudioSettings.enabled();
|
||||
bool enabled = !myAudioSettings.enabled();
|
||||
|
||||
setEnabled(!enabled);
|
||||
setEnabled(enabled);
|
||||
myOSystem.console().initializeAudio();
|
||||
|
||||
string message = "Sound ";
|
||||
message += !enabled ? "unmuted" : "muted";
|
||||
message += enabled ? "unmuted" : "muted";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -214,20 +259,12 @@ void SoundSDL2::setVolume(uInt32 percent)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void SoundSDL2::adjustVolume(Int8 direction)
|
||||
void SoundSDL2::adjustVolume(int direction)
|
||||
{
|
||||
ostringstream strval;
|
||||
string message;
|
||||
|
||||
Int32 percent = myVolume;
|
||||
|
||||
if(direction == -1)
|
||||
percent -= 2;
|
||||
else if(direction == 1)
|
||||
percent += 2;
|
||||
|
||||
if((percent < 0) || (percent > 100))
|
||||
return;
|
||||
percent = BSPF::clamp(percent + direction * 2, 0, 100);
|
||||
|
||||
setVolume(percent);
|
||||
|
||||
|
@ -241,11 +278,11 @@ void SoundSDL2::adjustVolume(Int8 direction)
|
|||
}
|
||||
|
||||
// Now show an onscreen message
|
||||
strval << percent;
|
||||
message = "Volume set to ";
|
||||
message += strval.str();
|
||||
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
if(percent)
|
||||
strval << percent << "%";
|
||||
else
|
||||
strval << "Off";
|
||||
myOSystem.frameBuffer().showMessage("Volume", strval.str(), percent);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -254,6 +291,7 @@ string SoundSDL2::about() const
|
|||
ostringstream buf;
|
||||
buf << "Sound enabled:" << endl
|
||||
<< " Volume: " << myVolume << "%" << endl
|
||||
<< " Device: " << myDevices.at(myDeviceId).first << endl
|
||||
<< " Channels: " << uInt32(myHardwareSpec.channels)
|
||||
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << endl
|
||||
<< " Preset: ";
|
||||
|
|
|
@ -24,12 +24,12 @@ class OSystem;
|
|||
class AudioQueue;
|
||||
class EmulationTiming;
|
||||
class AudioSettings;
|
||||
class Resampler;
|
||||
|
||||
#include "SDL_lib.hxx"
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Sound.hxx"
|
||||
#include "audio/Resampler.hxx"
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
@param direction Increase or decrease the current volume by a predefined
|
||||
amount based on the direction (1 = increase, -1 = decrease)
|
||||
*/
|
||||
void adjustVolume(Int8 direction) override;
|
||||
@param direction +1 indicates increase, -1 indicates decrease.
|
||||
*/
|
||||
void adjustVolume(int direction = 1) override;
|
||||
|
||||
/**
|
||||
This method is called to provide information about the sound device.
|
||||
|
@ -109,6 +108,13 @@ class SoundSDL2 : public Sound
|
|||
string about() const override;
|
||||
|
||||
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.
|
||||
The stream is 16-bits (even though the callback is 8-bits), since
|
||||
|
@ -140,6 +146,8 @@ class SoundSDL2 : public Sound
|
|||
// Audio specification structure
|
||||
SDL_AudioSpec myHardwareSpec;
|
||||
|
||||
uInt32 myDeviceId{0};
|
||||
|
||||
SDL_AudioDeviceID myDevice{0};
|
||||
|
||||
shared_ptr<AudioQueue> myAudioQueue;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
*/
|
||||
namespace Common {
|
||||
|
||||
template <class T, uInt32 CAPACITY = 50>
|
||||
template <typename T, uInt32 CAPACITY = 50>
|
||||
class FixedStack
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -299,15 +299,14 @@ void StateManager::saveState(int slot)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void StateManager::changeState(int direction)
|
||||
{
|
||||
myCurrentSlot += direction;
|
||||
if (myCurrentSlot < 0)
|
||||
myCurrentSlot = 9;
|
||||
else
|
||||
myCurrentSlot %= 10;
|
||||
myCurrentSlot = BSPF::clampw(myCurrentSlot + direction, 0, 9);
|
||||
|
||||
// Print appropriate message
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef STATE_MANAGER_HXX
|
||||
#define STATE_MANAGER_HXX
|
||||
|
||||
#define STATE_HEADER "06000008state"
|
||||
#define STATE_HEADER "06020100state"
|
||||
|
||||
class OSystem;
|
||||
class RewindManager;
|
||||
|
@ -115,8 +115,10 @@ class StateManager
|
|||
|
||||
/**
|
||||
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.
|
||||
|
|
|
@ -22,19 +22,19 @@
|
|||
|
||||
namespace Vec {
|
||||
|
||||
template<class T>
|
||||
template<typename T>
|
||||
void append(vector<T>& dst, const vector<T>& src)
|
||||
{
|
||||
dst.insert(dst.cend(), src.cbegin(), src.cend());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<typename T>
|
||||
void insertAt(vector<T>& dst, uInt32 idx, const T& element)
|
||||
{
|
||||
dst.insert(dst.cbegin()+idx, element);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<typename T>
|
||||
void removeAt(vector<T>& dst, uInt32 idx)
|
||||
{
|
||||
dst.erase(dst.cbegin()+idx);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef VERSION_HXX
|
||||
#define VERSION_HXX
|
||||
|
||||
#define STELLA_VERSION "6.1_rc1"
|
||||
#define STELLA_BUILD "5657"
|
||||
#define STELLA_VERSION "6.3_pre"
|
||||
#define STELLA_BUILD "6091"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -297,7 +297,7 @@ class ZipHandler
|
|||
void addToCache();
|
||||
|
||||
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
|
||||
|
||||
ZipFilePtr myZip;
|
||||
|
|
|
@ -43,6 +43,7 @@ using uInt64 = uint64_t;
|
|||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -63,8 +64,6 @@ using std::istream;
|
|||
using std::ostream;
|
||||
using std::fstream;
|
||||
using std::iostream;
|
||||
using std::ifstream;
|
||||
using std::ofstream;
|
||||
using std::ostringstream;
|
||||
using std::istringstream;
|
||||
using std::stringstream;
|
||||
|
@ -84,11 +83,14 @@ using ByteArray = std::vector<uInt8>;
|
|||
using ShortArray = std::vector<uInt16>;
|
||||
using StringList = std::vector<std::string>;
|
||||
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
|
||||
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("");
|
||||
|
@ -97,6 +99,12 @@ static const string EmptyString("");
|
|||
#undef PAGE_SIZE
|
||||
#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
|
||||
{
|
||||
static constexpr float PI_f = 3.141592653589793238462643383279502884F;
|
||||
|
@ -118,20 +126,39 @@ namespace BSPF
|
|||
static const string ARCH = "NOARCH";
|
||||
#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
|
||||
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>;
|
||||
|
||||
// Combines 'max' and 'min', and clamps value to the upper/lower value
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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
|
||||
inline const string& toUpperCase(string& s)
|
||||
|
|
|
@ -12,6 +12,7 @@ MODULE_OBJS := \
|
|||
src/common/Logger.o \
|
||||
src/common/main.o \
|
||||
src/common/MouseControl.o \
|
||||
src/common/PaletteHandler.o \
|
||||
src/common/PhosphorHandler.o \
|
||||
src/common/PhysicalJoystick.o \
|
||||
src/common/PJoystickHandler.o \
|
||||
|
|
|
@ -28,8 +28,8 @@ namespace {
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const string& filename)
|
||||
: myFilename(filename)
|
||||
KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const FilesystemNode& file)
|
||||
: myFile(file)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -41,10 +41,14 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
|
|||
string line, key, value;
|
||||
string::size_type equalPos, garbage;
|
||||
|
||||
ifstream in(myFilename);
|
||||
if(!in || !in.is_open()) {
|
||||
Logger::error("ERROR: Couldn't load from settings file " + myFilename);
|
||||
|
||||
stringstream in;
|
||||
try
|
||||
{
|
||||
myFile.read(in);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Logger::error("ERROR: Couldn't load from settings file " + myFile.getShortPath());
|
||||
return values;
|
||||
}
|
||||
|
||||
|
@ -79,13 +83,7 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
|
||||
{
|
||||
ofstream out(myFilename);
|
||||
if(!out || !out.is_open()) {
|
||||
Logger::error("ERROR: Couldn't save to settings file " + myFilename);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stringstream out;
|
||||
out << "; Stella configuration file" << endl
|
||||
<< ";" << 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
|
||||
for(const auto& pair: values)
|
||||
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
|
||||
#define KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
||||
|
||||
#include "FSNode.hxx"
|
||||
#include "KeyValueRepository.hxx"
|
||||
|
||||
class KeyValueRepositoryConfigfile : public KeyValueRepository
|
||||
{
|
||||
public:
|
||||
|
||||
explicit KeyValueRepositoryConfigfile(const string& filename);
|
||||
explicit KeyValueRepositoryConfigfile(const FilesystemNode& file);
|
||||
|
||||
std::map<string, Variant> load() override;
|
||||
|
||||
|
@ -34,7 +35,7 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository
|
|||
|
||||
private:
|
||||
|
||||
const string& myFilename;
|
||||
FilesystemNode myFile;
|
||||
};
|
||||
|
||||
#endif // KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "BilinearBlitter.hxx"
|
||||
|
||||
#include "FrameBufferSDL2.hxx"
|
||||
#include "ThreadDebugging.hxx"
|
||||
#include "BilinearBlitter.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb, bool interpolate)
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
#ifndef BILINEAR_BLITTER_HXX
|
||||
#define BILINEAR_BLITTER_HXX
|
||||
|
||||
class FrameBufferSDL2;
|
||||
|
||||
#include "Blitter.hxx"
|
||||
#include "FrameBufferSDL2.hxx"
|
||||
#include "SDL_lib.hxx"
|
||||
|
||||
class BilinearBlitter : public Blitter {
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "QisBlitter.hxx"
|
||||
|
||||
#include "FrameBufferSDL2.hxx"
|
||||
#include "ThreadDebugging.hxx"
|
||||
#include "QisBlitter.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
QisBlitter::QisBlitter(FrameBufferSDL2& fb)
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
#ifndef QIS_BLITTER_HXX
|
||||
#define QIS_BLITTER_HXX
|
||||
|
||||
class FrameBufferSDL2;
|
||||
|
||||
#include "Blitter.hxx"
|
||||
#include "FrameBufferSDL2.hxx"
|
||||
#include "SDL_lib.hxx"
|
||||
|
||||
class QisBlitter : public Blitter {
|
||||
|
|
|
@ -57,9 +57,15 @@ void AtariNTSC::generateKernels()
|
|||
const uInt8* ptr = myRGBPalette.data();
|
||||
for(size_t entry = 0; entry < myRGBPalette.size() / 3; ++entry)
|
||||
{
|
||||
#ifdef BLARGG_PALETTE
|
||||
float r = myImpl.to_float[*ptr++],
|
||||
g = 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 );
|
||||
|
||||
// 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)
|
||||
{
|
||||
#ifdef BLARGG_PALETTE
|
||||
impl.brightness = setup.brightness * (0.5F * rgb_unit) + rgb_offset;
|
||||
impl.contrast = setup.contrast * (0.5F * rgb_unit) + rgb_unit;
|
||||
#endif
|
||||
|
||||
impl.artifacts = setup.artifacts;
|
||||
if ( impl.artifacts > 0 )
|
||||
|
@ -334,6 +342,7 @@ void AtariNTSC::init(init_t& impl, const Setup& setup)
|
|||
|
||||
initFilters(impl, setup);
|
||||
|
||||
#ifdef BLARGG_PALETTE
|
||||
/* generate gamma table */
|
||||
if (true) /* was (gamma_size > 1) */
|
||||
{
|
||||
|
@ -341,19 +350,22 @@ void AtariNTSC::init(init_t& impl, const Setup& setup)
|
|||
float const gamma = 1.1333F - setup.gamma * 0.5F;
|
||||
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
|
||||
int i;
|
||||
for ( i = 0; i < gamma_size; i++ )
|
||||
impl.to_float [i] =
|
||||
powf( i * to_float, gamma ) * impl.contrast + impl.brightness;
|
||||
for(i = 0; i < gamma_size; i++)
|
||||
impl.to_float[i] =
|
||||
powf(i * to_float, gamma) * impl.contrast + impl.brightness;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* setup decoder matricies */
|
||||
{
|
||||
#ifdef BLARGG_PALETTE
|
||||
float hue = setup.hue * BSPF::PI_f + BSPF::PI_f / 180 * ext_decoder_hue;
|
||||
float sat = setup.saturation + 1;
|
||||
hue += BSPF::PI_f / 180 * (std_decoder_hue - ext_decoder_hue);
|
||||
|
||||
float s = sinf( hue ) * sat;
|
||||
float c = cosf( hue ) * sat;
|
||||
float s = sinf(hue)*sat;
|
||||
float c = cosf(hue)*sat;
|
||||
#endif
|
||||
float* out = impl.to_rgb.data();
|
||||
int n;
|
||||
|
||||
|
@ -366,8 +378,13 @@ void AtariNTSC::init(init_t& impl, const Setup& setup)
|
|||
{
|
||||
float i = *in++;
|
||||
float q = *in++;
|
||||
#ifdef BLARGG_PALETTE
|
||||
*out++ = i * c - q * s;
|
||||
*out++ = i * s + q * c;
|
||||
#else
|
||||
*out++ = i ;
|
||||
*out++ = q;
|
||||
#endif
|
||||
}
|
||||
while ( --n2 );
|
||||
#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 = {
|
||||
#ifdef BLARGG_PALETTE
|
||||
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 = {
|
||||
#ifdef BLARGG_PALETTE
|
||||
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 = {
|
||||
#ifdef BLARGG_PALETTE
|
||||
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 = {
|
||||
#ifdef BLARGG_PALETTE
|
||||
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 "bspf.hxx"
|
||||
|
||||
//#define BLARGG_PALETTE // also modify contrast, brightness, saturation, gamma and hue when defined
|
||||
|
||||
class AtariNTSC
|
||||
{
|
||||
public:
|
||||
|
@ -57,14 +59,18 @@ class AtariNTSC
|
|||
struct Setup
|
||||
{
|
||||
// Basic parameters
|
||||
#ifdef BLARGG_PALETTE
|
||||
float hue{0.F}; // -1 = -180 degrees +1 = +180 degrees
|
||||
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 brightness{0.F}; // -1 = dark (0.5) +1 = light (1.5)
|
||||
#endif
|
||||
float sharpness{0.F}; // edge contrast enhancement/blurring
|
||||
|
||||
// Advanced parameters
|
||||
#ifdef BLARGG_PALETTE
|
||||
float gamma{0.F}; // -1 = dark (1.5) +1 = light (0.5)
|
||||
#endif
|
||||
float resolution{0.F}; // image resolution
|
||||
float artifacts{0.F}; // artifacts caused by color changes
|
||||
float fringing{0.F}; // color artifacts caused by brightness changes
|
||||
|
@ -127,7 +133,9 @@ class AtariNTSC
|
|||
burst_size = entry_size / burst_count,
|
||||
kernel_half = 16,
|
||||
kernel_size = kernel_half * 2 + 1,
|
||||
#ifdef BLARGG_PALETTE
|
||||
gamma_size = 256,
|
||||
#endif
|
||||
|
||||
rgb_builder = ((1 << 21) | (1 << 11) | (1 << 1)),
|
||||
rgb_kernel_size = burst_size / alignment_count,
|
||||
|
@ -162,16 +170,20 @@ class AtariNTSC
|
|||
struct init_t
|
||||
{
|
||||
std::array<float, burst_count * 6> to_rgb{0.F};
|
||||
#ifdef BLARGG_PALETTE
|
||||
std::array<float, gamma_size> to_float{0.F};
|
||||
float contrast{0.F};
|
||||
float brightness{0.F};
|
||||
#endif
|
||||
float artifacts{0.F};
|
||||
float fringing{0.F};
|
||||
std::array<float, rescale_out * kernel_size * 2> kernel{0.F};
|
||||
|
||||
init_t() {
|
||||
to_rgb.fill(0.0);
|
||||
#ifdef BLARGG_PALETTE
|
||||
to_float.fill(0.0);
|
||||
#endif
|
||||
kernel.fill(0.0);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
#include "NTSCFilter.hxx"
|
||||
|
||||
constexpr float scaleFrom100(float x) { return (x/50.F) - 1.F; }
|
||||
constexpr uInt32 scaleTo100(float x) { return uInt32(50*(x+1.F)); }
|
||||
constexpr float scaleFrom100(float x) { return (x / 50.F) - 1.F; }
|
||||
constexpr uInt32 scaleTo100(float x) { return uInt32(50.0001F * (x + 1.F)); }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string NTSCFilter::setPreset(Preset preset)
|
||||
|
@ -62,9 +62,9 @@ string NTSCFilter::getPreset() const
|
|||
{
|
||||
switch(myPreset)
|
||||
{
|
||||
case Preset::COMPOSITE: return "COMPOSITE";
|
||||
case Preset::SVIDEO: return "S-VIDEO";
|
||||
case Preset::RGB: return "RGB";
|
||||
case Preset::SVIDEO: return "S-VIDEO";
|
||||
case Preset::COMPOSITE: return "COMPOSITE";
|
||||
case Preset::BAD: return "BAD ADJUST";
|
||||
case Preset::CUSTOM: return "CUSTOM";
|
||||
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)
|
||||
return "'Custom' TV mode not selected";
|
||||
if(direction == +1)
|
||||
{
|
||||
#ifdef BLARGG_PALETTE
|
||||
myCurrentAdjustable = (myCurrentAdjustable + 1) % 10;
|
||||
#else
|
||||
myCurrentAdjustable = (myCurrentAdjustable + 1) % 5;
|
||||
#endif
|
||||
}
|
||||
else if(direction == -1)
|
||||
{
|
||||
#ifdef BLARGG_PALETTE
|
||||
if(myCurrentAdjustable == 0) myCurrentAdjustable = 9;
|
||||
#else
|
||||
if(myCurrentAdjustable == 0) myCurrentAdjustable = 4;
|
||||
#endif
|
||||
else --myCurrentAdjustable;
|
||||
}
|
||||
|
||||
myCurrentAdjustable = (myCurrentAdjustable + 1) % 10;
|
||||
ostringstream buf;
|
||||
buf << "Custom adjustable '" << ourCustomAdjustables[myCurrentAdjustable].type
|
||||
<< "' selected";
|
||||
ostringstream msg, val;
|
||||
|
||||
return buf.str();
|
||||
value = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
||||
msg << "Custom " << ourCustomAdjustables[myCurrentAdjustable].type;
|
||||
val << value << "%";
|
||||
|
||||
text = msg.str();
|
||||
valueText = val.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string NTSCFilter::setPreviousAdjustable()
|
||||
void NTSCFilter::changeAdjustable(int adjustable, int direction,
|
||||
string& text, string& valueText, Int32& newValue)
|
||||
{
|
||||
if(myPreset != Preset::CUSTOM)
|
||||
return "'Custom' TV mode not selected";
|
||||
|
||||
if(myCurrentAdjustable == 0) myCurrentAdjustable = 9;
|
||||
else --myCurrentAdjustable;
|
||||
ostringstream buf;
|
||||
buf << "Custom adjustable '" << ourCustomAdjustables[myCurrentAdjustable].type
|
||||
<< "' selected";
|
||||
|
||||
return buf.str();
|
||||
myCurrentAdjustable = adjustable;
|
||||
changeCurrentAdjustable(direction, text, valueText, newValue);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string NTSCFilter::increaseAdjustable()
|
||||
void NTSCFilter::changeCurrentAdjustable(int direction,
|
||||
string& text, string& valueText, Int32& newValue)
|
||||
{
|
||||
if(myPreset != Preset::CUSTOM)
|
||||
return "'Custom' TV mode not selected";
|
||||
//if(myPreset != Preset::CUSTOM)
|
||||
// return "'Custom' TV mode not selected";
|
||||
|
||||
uInt32 newval = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
||||
newval += 2; if(newval > 100) newval = 100;
|
||||
*ourCustomAdjustables[myCurrentAdjustable].value = scaleFrom100(newval);
|
||||
newValue = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
||||
newValue = BSPF::clamp(newValue + direction * 1, 0, 100);
|
||||
|
||||
ostringstream buf;
|
||||
buf << "Custom '" << ourCustomAdjustables[myCurrentAdjustable].type
|
||||
<< "' set to " << newval;
|
||||
*ourCustomAdjustables[myCurrentAdjustable].value = scaleFrom100(newValue);
|
||||
|
||||
setPreset(myPreset);
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string NTSCFilter::decreaseAdjustable()
|
||||
{
|
||||
if(myPreset != Preset::CUSTOM)
|
||||
return "'Custom' TV mode not selected";
|
||||
ostringstream msg, val;
|
||||
|
||||
uInt32 newval = scaleTo100(*ourCustomAdjustables[myCurrentAdjustable].value);
|
||||
if(newval < 2) newval = 0;
|
||||
else newval -= 2;
|
||||
*ourCustomAdjustables[myCurrentAdjustable].value = scaleFrom100(newval);
|
||||
msg << "Custom " << ourCustomAdjustables[myCurrentAdjustable].type;
|
||||
val << newValue << "%";
|
||||
|
||||
ostringstream buf;
|
||||
buf << "Custom '" << ourCustomAdjustables[myCurrentAdjustable].type
|
||||
<< "' set to " << newval;
|
||||
|
||||
setPreset(myPreset);
|
||||
return buf.str();
|
||||
text = msg.str();
|
||||
valueText = val.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void NTSCFilter::loadConfig(const Settings& settings)
|
||||
{
|
||||
|
||||
// Load adjustables for custom mode
|
||||
#ifdef BLARGG_PALETTE
|
||||
myCustomSetup.hue = BSPF::clamp(settings.getFloat("tv.hue"), -1.0F, 1.0F);
|
||||
myCustomSetup.saturation = BSPF::clamp(settings.getFloat("tv.saturation"), -1.0F, 1.0F);
|
||||
myCustomSetup.contrast = BSPF::clamp(settings.getFloat("tv.contrast"), -1.0F, 1.0F);
|
||||
myCustomSetup.brightness = BSPF::clamp(settings.getFloat("tv.brightness"), -1.0F, 1.0F);
|
||||
myCustomSetup.sharpness = BSPF::clamp(settings.getFloat("tv.sharpness"), -1.0F, 1.0F);
|
||||
myCustomSetup.gamma = BSPF::clamp(settings.getFloat("tv.gamma"), -1.0F, 1.0F);
|
||||
#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.artifacts = BSPF::clamp(settings.getFloat("tv.artifacts"), -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
|
||||
{
|
||||
// Save adjustables for custom mode
|
||||
#ifdef BLARGG_PALETTE
|
||||
settings.setValue("tv.hue", myCustomSetup.hue);
|
||||
settings.setValue("tv.saturation", myCustomSetup.saturation);
|
||||
settings.setValue("tv.contrast", myCustomSetup.contrast);
|
||||
settings.setValue("tv.brightness", myCustomSetup.brightness);
|
||||
settings.setValue("tv.sharpness", myCustomSetup.sharpness);
|
||||
settings.setValue("tv.gamma", myCustomSetup.gamma);
|
||||
#endif
|
||||
settings.setValue("tv.sharpness", myCustomSetup.sharpness);
|
||||
settings.setValue("tv.resolution", myCustomSetup.resolution);
|
||||
settings.setValue("tv.artifacts", myCustomSetup.artifacts);
|
||||
settings.setValue("tv.fringing", myCustomSetup.fringing);
|
||||
|
@ -174,12 +176,12 @@ void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset) const
|
|||
{
|
||||
switch(preset)
|
||||
{
|
||||
case Preset::COMPOSITE:
|
||||
convertToAdjustable(adjustable, AtariNTSC::TV_Composite); break;
|
||||
case Preset::SVIDEO:
|
||||
convertToAdjustable(adjustable, AtariNTSC::TV_SVideo); break;
|
||||
case Preset::RGB:
|
||||
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:
|
||||
convertToAdjustable(adjustable, AtariNTSC::TV_Bad); break;
|
||||
case Preset::CUSTOM:
|
||||
|
@ -192,12 +194,14 @@ void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset) const
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void NTSCFilter::setCustomAdjustables(Adjustable& adjustable)
|
||||
{
|
||||
myCustomSetup.hue = scaleFrom100(adjustable.hue);
|
||||
myCustomSetup.saturation = scaleFrom100(adjustable.saturation);
|
||||
myCustomSetup.contrast = scaleFrom100(adjustable.contrast);
|
||||
myCustomSetup.brightness = scaleFrom100(adjustable.brightness);
|
||||
#ifdef BLARGG_PALETTE
|
||||
//myCustomSetup.hue = scaleFrom100(adjustable.hue);
|
||||
//myCustomSetup.saturation = scaleFrom100(adjustable.saturation);
|
||||
//myCustomSetup.contrast = scaleFrom100(adjustable.contrast);
|
||||
//myCustomSetup.brightness = scaleFrom100(adjustable.brightness);
|
||||
//myCustomSetup.gamma = scaleFrom100(adjustable.gamma);
|
||||
#endif
|
||||
myCustomSetup.sharpness = scaleFrom100(adjustable.sharpness);
|
||||
myCustomSetup.gamma = scaleFrom100(adjustable.gamma);
|
||||
myCustomSetup.resolution = scaleFrom100(adjustable.resolution);
|
||||
myCustomSetup.artifacts = scaleFrom100(adjustable.artifacts);
|
||||
myCustomSetup.fringing = scaleFrom100(adjustable.fringing);
|
||||
|
@ -208,12 +212,14 @@ void NTSCFilter::setCustomAdjustables(Adjustable& adjustable)
|
|||
void NTSCFilter::convertToAdjustable(Adjustable& adjustable,
|
||||
const AtariNTSC::Setup& setup) const
|
||||
{
|
||||
adjustable.hue = scaleTo100(setup.hue);
|
||||
adjustable.saturation = scaleTo100(setup.saturation);
|
||||
adjustable.contrast = scaleTo100(setup.contrast);
|
||||
adjustable.brightness = scaleTo100(setup.brightness);
|
||||
#ifdef BLARGG_PALETTE
|
||||
//adjustable.hue = scaleTo100(setup.hue);
|
||||
//adjustable.saturation = scaleTo100(setup.saturation);
|
||||
//adjustable.contrast = scaleTo100(setup.contrast);
|
||||
//adjustable.brightness = scaleTo100(setup.brightness);
|
||||
//adjustable.gamma = scaleTo100(setup.gamma);
|
||||
#endif
|
||||
adjustable.sharpness = scaleTo100(setup.sharpness);
|
||||
adjustable.gamma = scaleTo100(setup.gamma);
|
||||
adjustable.resolution = scaleTo100(setup.resolution);
|
||||
adjustable.artifacts = scaleTo100(setup.artifacts);
|
||||
adjustable.fringing = scaleTo100(setup.fringing);
|
||||
|
@ -224,12 +230,16 @@ void NTSCFilter::convertToAdjustable(Adjustable& adjustable,
|
|||
AtariNTSC::Setup NTSCFilter::myCustomSetup = AtariNTSC::TV_Composite;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
#ifdef BLARGG_PALETTE
|
||||
const std::array<NTSCFilter::AdjustableTag, 10> NTSCFilter::ourCustomAdjustables = { {
|
||||
{ "contrast", &myCustomSetup.contrast },
|
||||
{ "brightness", &myCustomSetup.brightness },
|
||||
{ "hue", &myCustomSetup.hue },
|
||||
{ "saturation", &myCustomSetup.saturation },
|
||||
{ "gamma", &myCustomSetup.gamma },
|
||||
#else
|
||||
const std::array<NTSCFilter::AdjustableTag, int(NTSCFilter::Adjustables::NUM_ADJUSTABLES)> NTSCFilter::ourCustomAdjustables = { {
|
||||
#endif
|
||||
{ "sharpness", &myCustomSetup.sharpness },
|
||||
{ "resolution", &myCustomSetup.resolution },
|
||||
{ "artifacts", &myCustomSetup.artifacts },
|
||||
|
|
|
@ -47,12 +47,24 @@ class NTSCFilter
|
|||
BAD,
|
||||
CUSTOM
|
||||
};
|
||||
enum class Adjustables {
|
||||
SHARPNESS,
|
||||
RESOLUTION,
|
||||
ARTIFACTS,
|
||||
FRINGING,
|
||||
BLEEDING,
|
||||
NUM_ADJUSTABLES
|
||||
};
|
||||
|
||||
/* Normally used in conjunction with custom mode, contains all
|
||||
aspects currently adjustable in NTSC TV emulation. */
|
||||
struct Adjustable {
|
||||
#ifdef BLARGG_PALETTE
|
||||
uInt32 hue, saturation, contrast, brightness, gamma,
|
||||
sharpness, resolution, artifacts, fringing, bleed;
|
||||
#else
|
||||
uInt32 sharpness, resolution, artifacts, fringing, bleed;
|
||||
#endif
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -86,10 +98,12 @@ class NTSCFilter
|
|||
// Changes are made this way since otherwise 20 key-combinations
|
||||
// would be needed to dynamically change each setting, and now
|
||||
// only 4 combinations are necessary
|
||||
string setNextAdjustable();
|
||||
string setPreviousAdjustable();
|
||||
string increaseAdjustable();
|
||||
string decreaseAdjustable();
|
||||
void selectAdjustable(int direction,
|
||||
string& text, string& valueText, Int32& value);
|
||||
void changeAdjustable(int adjustable, int direction,
|
||||
string& text, string& valueText, Int32& newValue);
|
||||
void changeCurrentAdjustable(int direction,
|
||||
string& text, string& valueText, Int32& newValue);
|
||||
|
||||
// Load and save NTSC-related settings
|
||||
void loadConfig(const Settings& settings);
|
||||
|
@ -139,7 +153,11 @@ class NTSCFilter
|
|||
float* value{nullptr};
|
||||
};
|
||||
uInt32 myCurrentAdjustable{0};
|
||||
#ifdef BLARGG_PALETTE
|
||||
static const std::array<AdjustableTag, 10> ourCustomAdjustables;
|
||||
#else
|
||||
static const std::array<AdjustableTag, 5> ourCustomAdjustables;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
|
|
|
@ -32,7 +32,10 @@
|
|||
#include "CartRamWidget.hxx"
|
||||
#include "RomWidget.hxx"
|
||||
#include "Base.hxx"
|
||||
#include "Device.hxx"
|
||||
#include "exception/EmulationWarning.hxx"
|
||||
#include "TIA.hxx"
|
||||
#include "M6532.hxx"
|
||||
|
||||
using Common::Base;
|
||||
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
|
||||
// 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
|
||||
// Banksizes less than 4K use the actual value
|
||||
size_t banksize = 0;
|
||||
myConsole.cartridge().getImage(banksize);
|
||||
// ROM sizes less than 4K use the actual value
|
||||
size_t romSize = 0;
|
||||
myConsole.cartridge().getImage(romSize);
|
||||
|
||||
BankInfo info;
|
||||
info.size = std::min<size_t>(banksize, 4_KB);
|
||||
for(uInt32 i = 0; i < myConsole.cartridge().bankCount(); ++i)
|
||||
info.size = std::min<size_t>(romSize, 4_KB);
|
||||
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);
|
||||
|
||||
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
|
||||
myBankInfo[myConsole.cartridge().startBank()].addressList.push_front(
|
||||
myDebugger.dpeek(0xfffc));
|
||||
addLabel("Start", myDebugger.dpeek(0xfffc, DATA));
|
||||
addLabel("Start", myDebugger.dpeek(0xfffc, Device::DATA)); // TOOD: ::CODE???
|
||||
|
||||
// Add system equates
|
||||
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()
|
||||
{
|
||||
|
@ -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
|
||||
// Also check if the current PC is in the current list
|
||||
bool bankChanged = myConsole.cartridge().bankChanged();
|
||||
uInt16 PC = myDebugger.cpuDebug().pc();
|
||||
int pcline = addressToLine(PC);
|
||||
bool pcfound = (pcline != -1) && (uInt32(pcline) < myDisassembly.list.size()) &&
|
||||
(myDisassembly.list[pcline].disasm[0] != '.');
|
||||
|
@ -241,8 +283,9 @@ bool CartDebug::disassemble(bool force)
|
|||
if(changed)
|
||||
{
|
||||
// Are we disassembling from ROM or ZP RAM?
|
||||
BankInfo& info = (PC & 0x1000) ? myBankInfo[getBank(PC)] :
|
||||
myBankInfo[myBankInfo.size()-1];
|
||||
BankInfo& info = myBankInfo[bank];
|
||||
//(PC & 0x1000) ? myBankInfo[getBank(PC)] :
|
||||
//myBankInfo[myBankInfo.size()-1];
|
||||
|
||||
// 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
|
||||
|
@ -304,8 +347,8 @@ bool CartDebug::fillDisassemblyList(BankInfo& info, uInt16 search)
|
|||
const DisassemblyTag& tag = myDisassembly.list[i];
|
||||
const uInt16 address = tag.address & 0xFFF;
|
||||
|
||||
// Exclude 'ROW'; they don't have a valid address
|
||||
if(tag.type != CartDebug::ROW)
|
||||
// Exclude 'Device::ROW'; they don't have a valid address
|
||||
if(tag.type != Device::ROW)
|
||||
{
|
||||
// Create a mapping from addresses to line numbers
|
||||
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
|
||||
start &= 0xFFF;
|
||||
|
@ -346,7 +389,7 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
|||
if((tag.address & 0xfff) >= start)
|
||||
{
|
||||
if(begin == list_size) begin = end;
|
||||
if(tag.type != CartDebug::ROW)
|
||||
if(tag.type != Device::ROW)
|
||||
length = std::max(length, uInt32(tag.disasm.length()));
|
||||
|
||||
--lines;
|
||||
|
@ -357,7 +400,7 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
|||
for(uInt32 i = begin; i < end; ++i)
|
||||
{
|
||||
const CartDebug::DisassemblyTag& tag = myDisassembly.list[i];
|
||||
if(tag.type == CartDebug::NONE)
|
||||
if(tag.type == Device::NONE)
|
||||
continue;
|
||||
else if(tag.address)
|
||||
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)
|
||||
{
|
||||
if(end < start || start == 0 || end == 0)
|
||||
|
@ -384,7 +427,7 @@ bool CartDebug::addDirective(CartDebug::DisasmType type,
|
|||
bank = (myDebugger.cpuDebug().pc() & 0x1000) ?
|
||||
getBank(myDebugger.cpuDebug().pc()) : int(myBankInfo.size())-1;
|
||||
|
||||
bank = std::min(bank, bankCount());
|
||||
bank = std::min(bank, romBankCount());
|
||||
BankInfo& info = myBankInfo[bank];
|
||||
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
|
||||
// actual ROM filename
|
||||
|
||||
if(myListFile == "")
|
||||
{
|
||||
FilesystemNode lst(myOSystem.romFile().getPathWithExt("") + ".lst");
|
||||
if(lst.isFile() && lst.isReadable())
|
||||
myListFile = lst.getPath();
|
||||
else
|
||||
return DebuggerParser::red("list file \'" + lst.getShortPath() + "\' not found");
|
||||
}
|
||||
FilesystemNode lst(myOSystem.romFile().getPathWithExt(".lst"));
|
||||
if(!lst.isReadable())
|
||||
return DebuggerParser::red("list file \'" + lst.getShortPath() + "\' not found");
|
||||
|
||||
FilesystemNode node(myListFile);
|
||||
ifstream in(node.getPath());
|
||||
if(!in.is_open())
|
||||
return DebuggerParser::red("list file '" + node.getShortPath() + "' not readable");
|
||||
stringstream in;
|
||||
try
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
@ -747,7 +791,7 @@ string CartDebug::loadListFile()
|
|||
}
|
||||
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
|
||||
// actual ROM filename
|
||||
|
||||
if(mySymbolFile == "")
|
||||
{
|
||||
FilesystemNode sym(myOSystem.romFile().getPathWithExt("") + ".sym");
|
||||
if(sym.isFile() && sym.isReadable())
|
||||
mySymbolFile = sym.getPath();
|
||||
else
|
||||
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");
|
||||
FilesystemNode sym(myOSystem.romFile().getPathWithExt(".sym"));
|
||||
if(!sym.isReadable())
|
||||
return DebuggerParser::red("symbol file \'" + sym.getShortPath() + "\' not found");
|
||||
|
||||
myUserAddresses.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())
|
||||
{
|
||||
string label;
|
||||
|
@ -807,28 +852,29 @@ string CartDebug::loadSymbolFile()
|
|||
}
|
||||
myDebugger.rom().invalidate();
|
||||
|
||||
return "symbol file '" + node.getShortPath() + "' loaded OK";
|
||||
return "symbol file '" + sym.getShortPath() + "' loaded OK";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::loadConfigFile()
|
||||
{
|
||||
// The default naming/location for config files is the ROM dir based on the
|
||||
// actual ROM filename
|
||||
// The default naming/location for config files is the CFG dir and based
|
||||
// on the actual ROM filename
|
||||
|
||||
if(myCfgFile == "")
|
||||
FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg"));
|
||||
FilesystemNode cfg = myOSystem.cfgDir(); cfg /= romNode.getName();
|
||||
if(!cfg.isReadable())
|
||||
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not found");
|
||||
|
||||
stringstream in;
|
||||
try
|
||||
{
|
||||
FilesystemNode cfg(myOSystem.romFile().getPathWithExt("") + ".cfg");
|
||||
if(cfg.isFile() && cfg.isReadable())
|
||||
myCfgFile = cfg.getPath();
|
||||
else
|
||||
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not found");
|
||||
cfg.read(in);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return "Unable to load directives from " + cfg.getPath();
|
||||
}
|
||||
|
||||
FilesystemNode node(myCfgFile);
|
||||
ifstream in(node.getPath());
|
||||
if(!in.is_open())
|
||||
return "Unable to load directives from " + node.getPath();
|
||||
|
||||
// Erase all previous directives
|
||||
for(auto& bi: myBankInfo)
|
||||
|
@ -876,37 +922,57 @@ string CartDebug::loadConfigFile()
|
|||
else if(BSPF::startsWithIgnoreCase(directive, "CODE"))
|
||||
{
|
||||
buf >> hex >> start >> hex >> end;
|
||||
addDirective(CartDebug::CODE, start, end, currentbank);
|
||||
addDirective(Device::CODE, start, end, currentbank);
|
||||
}
|
||||
else if(BSPF::startsWithIgnoreCase(directive, "GFX"))
|
||||
{
|
||||
buf >> hex >> start >> hex >> end;
|
||||
addDirective(CartDebug::GFX, start, end, currentbank);
|
||||
addDirective(Device::GFX, start, end, currentbank);
|
||||
}
|
||||
else if(BSPF::startsWithIgnoreCase(directive, "PGFX"))
|
||||
{
|
||||
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"))
|
||||
{
|
||||
buf >> hex >> start >> hex >> end;
|
||||
addDirective(CartDebug::DATA, start, end, currentbank);
|
||||
addDirective(Device::DATA, start, end, currentbank);
|
||||
}
|
||||
else if(BSPF::startsWithIgnoreCase(directive, "ROW"))
|
||||
{
|
||||
buf >> hex >> start;
|
||||
buf >> hex >> end;
|
||||
addDirective(CartDebug::ROW, start, end, currentbank);
|
||||
addDirective(Device::ROW, start, end, currentbank);
|
||||
}
|
||||
}
|
||||
}
|
||||
myDebugger.rom().invalidate();
|
||||
|
||||
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 << "config file '" << node.getShortPath() << "' loaded OK";
|
||||
retVal << "config file '" << cfg.getShortPath() << "' loaded OK";
|
||||
return retVal.str();
|
||||
|
||||
}
|
||||
|
@ -914,69 +980,72 @@ string CartDebug::loadConfigFile()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::saveConfigFile()
|
||||
{
|
||||
// The default naming/location for config files is the ROM dir based 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");
|
||||
}
|
||||
// The default naming/location for config files is the CFG dir and based
|
||||
// on the actual ROM filename
|
||||
|
||||
const string& name = myConsole.properties().get(PropType::Cart_Name);
|
||||
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
|
||||
out << "//Stella.pro: \"" << name << "\"" << endl
|
||||
<< "//MD5: " << md5 << endl
|
||||
stringstream out;
|
||||
out << "// Stella.pro: \"" << name << "\"" << endl
|
||||
<< "// MD5: " << md5 << endl
|
||||
<< endl;
|
||||
for(uInt32 b = 0; b < myConsole.cartridge().bankCount(); ++b)
|
||||
for(uInt32 b = 0; b < myConsole.cartridge().romBankCount(); ++b)
|
||||
{
|
||||
out << "[" << b << "]" << endl;
|
||||
getBankDirectives(out, myBankInfo[b]);
|
||||
}
|
||||
|
||||
stringstream retVal;
|
||||
if(myConsole.cartridge().bankCount() > 1)
|
||||
retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n");
|
||||
retVal << "config file '" << cfg.getShortPath() << "' saved OK";
|
||||
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 << "config file '" << cfg.getShortPath() << "' saved OK";
|
||||
}
|
||||
catch(const runtime_error& e)
|
||||
{
|
||||
retVal << "Unable to save directives: " << e.what();
|
||||
}
|
||||
return retVal.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::saveDisassembly()
|
||||
{
|
||||
if(myDisasmFile == "")
|
||||
{
|
||||
const string& propsname =
|
||||
myConsole.properties().get(PropType::Cart_Name) + ".asm";
|
||||
|
||||
myDisasmFile = FilesystemNode(myOSystem.defaultSaveDir() + propsname).getPath();
|
||||
}
|
||||
|
||||
FilesystemNode node(myDisasmFile);
|
||||
ofstream out(node.getPath());
|
||||
if(!out.is_open())
|
||||
return "Unable to save disassembly to " + node.getShortPath();
|
||||
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"
|
||||
};
|
||||
bool isNTSC = myConsole.timing() == ConsoleTiming::ntsc;
|
||||
bool isPAL = myConsole.timing() == ConsoleTiming::pal;
|
||||
|
||||
#define ALIGN(x) setfill(' ') << left << setw(x)
|
||||
|
||||
// We can't print the header to the disassembly until it's actually
|
||||
// been processed; therefore buffer output to a string first
|
||||
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
|
||||
// This will most likely differ from what you see in the debugger
|
||||
|
@ -992,13 +1061,34 @@ string CartDebug::saveDisassembly()
|
|||
|
||||
Disassembly disasm;
|
||||
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];
|
||||
|
||||
disassembleBank(bank);
|
||||
|
||||
// An empty address list means that DiStella can't do a disassembly
|
||||
if(info.addressList.size() == 0)
|
||||
continue;
|
||||
|
||||
buf << "\n\n;***********************************************************\n"
|
||||
<< "; Bank " << bank;
|
||||
if (romBankCount > 1)
|
||||
buf << " / 0.." << romBankCount - 1;
|
||||
buf << "\n;***********************************************************\n\n";
|
||||
|
||||
// Disassemble bank
|
||||
disasm.list.clear();
|
||||
DiStella distella(*this, disasm.list, info, settings,
|
||||
|
@ -1007,8 +1097,14 @@ string CartDebug::saveDisassembly()
|
|||
if (myReserved.breakFound)
|
||||
addLabel("Break", myDebugger.dpeek(0xfffe));
|
||||
|
||||
buf << " SEG CODE\n"
|
||||
<< " ORG $" << Base::HEX4 << info.offset << "\n\n";
|
||||
buf << " SEG CODE\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
|
||||
for(uInt32 i = 0; i < disasm.list.size(); ++i)
|
||||
|
@ -1022,76 +1118,115 @@ string CartDebug::saveDisassembly()
|
|||
|
||||
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);
|
||||
if (tag.disasm.find("WSYNC") != std::string::npos)
|
||||
buf << "\n;---------------------------------------";
|
||||
break;
|
||||
}
|
||||
case CartDebug::ROW:
|
||||
{
|
||||
|
||||
case Device::ROW:
|
||||
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8*4-1) << "; $" << Base::HEX4 << tag.address << " (*)";
|
||||
break;
|
||||
}
|
||||
case CartDebug::GFX:
|
||||
{
|
||||
|
||||
case Device::GFX:
|
||||
buf << ".byte " << (settings.gfxFormat == Base::Fmt::_2 ? "%" : "$")
|
||||
<< tag.bytes << " ; |";
|
||||
for(int c = 12; c < 20; ++c)
|
||||
buf << ((tag.disasm[c] == '\x1e') ? "#" : " ");
|
||||
buf << ALIGN(13) << "|" << "$" << Base::HEX4 << tag.address << " (G)";
|
||||
break;
|
||||
}
|
||||
case CartDebug::PGFX:
|
||||
{
|
||||
|
||||
case Device::PGFX:
|
||||
buf << ".byte " << (settings.gfxFormat == Base::Fmt::_2 ? "%" : "$")
|
||||
<< tag.bytes << " ; |";
|
||||
for(int c = 12; c < 20; ++c)
|
||||
buf << ((tag.disasm[c] == '\x1f') ? "*" : " ");
|
||||
buf << ALIGN(13) << "|" << "$" << Base::HEX4 << tag.address << " (P)";
|
||||
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)";
|
||||
break;
|
||||
}
|
||||
case CartDebug::NONE:
|
||||
|
||||
case Device::NONE:
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
buf << "\n";
|
||||
}
|
||||
}
|
||||
myConsole.cartridge().unlockBank();
|
||||
myConsole.cartridge().bank(oldBank);
|
||||
myConsole.cartridge().lockBank();
|
||||
|
||||
// Some boilerplate, similar to what DiStella adds
|
||||
auto timeinfo = BSPF::localTime();
|
||||
stringstream out;
|
||||
out << "; Disassembly of " << myOSystem.romFile().getShortPath() << "\n"
|
||||
<< "; Disassembled " << std::put_time(&timeinfo, "%c\n")
|
||||
<< "; Using Stella " << STELLA_VERSION << "\n;\n"
|
||||
<< "; ROM properties name : " << myConsole.properties().get(PropType::Cart_Name) << "\n"
|
||||
<< "; ROM properties MD5 : " << myConsole.properties().get(PropType::Cart_MD5) << "\n"
|
||||
<< "; Bankswitch type : " << myConsole.cartridge().about() << "\n;\n"
|
||||
<< "; Legend: * = CODE not yet run (tentative code)\n"
|
||||
<< "; D = DATA directive (referenced in some way)\n"
|
||||
<< "; G = GFX directive, shown as '#' (stored in player, missile, ball)\n"
|
||||
<< "; P = PGFX directive, shown as '*' (stored in playfield)\n"
|
||||
<< "; i = indexed accessed only\n"
|
||||
<< "; c = used by code executed in RAM\n"
|
||||
<< "; s = used by stack\n"
|
||||
<< "; ! = page crossed, 1 cycle penalty\n"
|
||||
<< "; Legend: * = CODE not yet run (tentative code)\n"
|
||||
<< "; D = DATA directive (referenced in some way)\n"
|
||||
<< "; G = GFX directive, shown as '#' (stored in player, missile, ball)\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"
|
||||
<< "; c = used by code executed in RAM\n"
|
||||
<< "; s = used by stack\n"
|
||||
<< "; ! = page crossed, 1 cycle penalty\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;
|
||||
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)
|
||||
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)
|
||||
addrUsed = addrUsed || myReserved.IOReadWrite[addr];
|
||||
|
||||
if(addrUsed)
|
||||
{
|
||||
out << "\n;-----------------------------------------------------------\n"
|
||||
|
@ -1103,7 +1238,7 @@ string CartDebug::saveDisassembly()
|
|||
if(myReserved.TIARead[addr] && ourTIAMnemonicR[addr])
|
||||
out << ALIGN(16) << ourTIAMnemonicR[addr] << "= $"
|
||||
<< 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] << "= $"
|
||||
<< Base::HEX2 << right << addr << " ; (Ri)\n";
|
||||
out << "\n";
|
||||
|
@ -1113,7 +1248,7 @@ string CartDebug::saveDisassembly()
|
|||
if(myReserved.TIAWrite[addr] && ourTIAMnemonicW[addr])
|
||||
out << ALIGN(16) << ourTIAMnemonicW[addr] << "= $"
|
||||
<< 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] << "= $"
|
||||
<< Base::HEX2 << right << addr << " ; (Wi)\n";
|
||||
out << "\n";
|
||||
|
@ -1128,8 +1263,8 @@ string CartDebug::saveDisassembly()
|
|||
addrUsed = false;
|
||||
for(uInt16 addr = 0x80; addr <= 0xFF; ++addr)
|
||||
addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80]
|
||||
|| (mySystem.getAccessFlags(addr) & (DATA | WRITE))
|
||||
|| (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
|
||||
|| (mySystem.getAccessFlags(addr) & (Device::DATA | Device::WRITE))
|
||||
|| (mySystem.getAccessFlags(addr|0x100) & (Device::DATA | Device::WRITE));
|
||||
if(addrUsed)
|
||||
{
|
||||
bool addLine = false;
|
||||
|
@ -1138,9 +1273,9 @@ string CartDebug::saveDisassembly()
|
|||
<< ";-----------------------------------------------------------\n\n";
|
||||
|
||||
for (uInt16 addr = 0x80; addr <= 0xFF; ++addr) {
|
||||
bool ramUsed = (mySystem.getAccessFlags(addr) & (DATA | WRITE));
|
||||
bool codeUsed = (mySystem.getAccessFlags(addr) & CODE);
|
||||
bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
|
||||
bool ramUsed = (mySystem.getAccessFlags(addr) & (Device::DATA | Device::WRITE));
|
||||
bool codeUsed = (mySystem.getAccessFlags(addr) & Device::CODE);
|
||||
bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (Device::DATA | Device::WRITE));
|
||||
|
||||
if (myReserved.ZPRAM[addr - 0x80] &&
|
||||
myUserLabels.find(addr) == myUserLabels.end()) {
|
||||
|
@ -1194,10 +1329,22 @@ string CartDebug::saveDisassembly()
|
|||
// And finally, output the disassembly
|
||||
out << buf.str();
|
||||
|
||||
const string& propsname =
|
||||
myConsole.properties().get(PropType::Cart_Name) + ".asm";
|
||||
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + propsname);
|
||||
stringstream retVal;
|
||||
if(myConsole.cartridge().bankCount() > 1)
|
||||
retVal << DebuggerParser::red("disassembly for multi-bank ROM not fully supported, only currently enabled banks disassembled\n");
|
||||
retVal << "saved " << node.getShortPath() << " OK";
|
||||
try
|
||||
{
|
||||
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";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
retVal << "Unable to save disassembly to " << node.getShortPath();
|
||||
}
|
||||
return retVal.str();
|
||||
}
|
||||
|
||||
|
@ -1206,22 +1353,40 @@ string CartDebug::saveRom()
|
|||
{
|
||||
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26";
|
||||
|
||||
FilesystemNode node(myOSystem.defaultSaveDir() + rom);
|
||||
ofstream out(node.getPath(), std::ios::binary);
|
||||
if(out && myConsole.cartridge().saveROM(out))
|
||||
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
|
||||
if(myConsole.cartridge().saveROM(node))
|
||||
return "saved ROM as " + node.getShortPath();
|
||||
else
|
||||
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)
|
||||
{
|
||||
if(myConsole.cartridge().bankCount() > 1)
|
||||
return DebuggerParser::red("config file for multi-bank ROM not yet supported");
|
||||
|
||||
uInt32 startbank = 0, endbank = bankCount();
|
||||
if(bank >= 0 && bank < bankCount())
|
||||
uInt32 startbank = 0, endbank = romBankCount();
|
||||
if(bank >= 0 && bank < romBankCount())
|
||||
{
|
||||
startbank = bank;
|
||||
endbank = startbank + 1;
|
||||
|
@ -1232,27 +1397,30 @@ string CartDebug::listConfig(int bank)
|
|||
for(uInt32 b = startbank; b < endbank; ++b)
|
||||
{
|
||||
BankInfo& info = myBankInfo[b];
|
||||
buf << "[" << b << "]" << endl;
|
||||
buf << "Bank [" << b << "]" << endl;
|
||||
for(const auto& i: info.directiveList)
|
||||
{
|
||||
if(i.type != CartDebug::NONE)
|
||||
if(i.type != Device::NONE)
|
||||
{
|
||||
buf << "(*) ";
|
||||
disasmTypeAsString(buf, i.type);
|
||||
AccessTypeAsString(buf, i.type);
|
||||
buf << " " << Base::HEX4 << i.start << " " << Base::HEX4 << i.end << endl;
|
||||
}
|
||||
}
|
||||
getBankDirectives(buf, info);
|
||||
}
|
||||
|
||||
if(myConsole.cartridge().romBankCount() > 1)
|
||||
buf << DebuggerParser::red("config file for multi-bank ROM not fully supported") << endl;
|
||||
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::clearConfig(int bank)
|
||||
{
|
||||
uInt32 startbank = 0, endbank = bankCount();
|
||||
if(bank >= 0 && bank < bankCount())
|
||||
uInt32 startbank = 0, endbank = romBankCount();
|
||||
if(bank >= 0 && bank < romBankCount())
|
||||
{
|
||||
startbank = bank;
|
||||
endbank = startbank + 1;
|
||||
|
@ -1334,15 +1502,15 @@ void CartDebug::getBankDirectives(ostream& buf, BankInfo& info) const
|
|||
|
||||
// Now consider each byte
|
||||
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)
|
||||
{
|
||||
DisasmType currType = disasmTypeAbsolute(mySystem.getAccessFlags(addr));
|
||||
Device::AccessType currType = accessTypeAbsolute(mySystem.getAccessFlags(addr));
|
||||
|
||||
// Have we changed to a new type?
|
||||
if(currType != prevType)
|
||||
{
|
||||
disasmTypeAsString(buf, prevType);
|
||||
AccessTypeAsString(buf, prevType);
|
||||
buf << " " << Base::HEX4 << prev << " " << Base::HEX4 << (addr-1) << endl;
|
||||
|
||||
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
|
||||
if(prev != addr)
|
||||
{
|
||||
disasmTypeAsString(buf, prevType);
|
||||
AccessTypeAsString(buf, prevType);
|
||||
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))
|
||||
{
|
||||
|
@ -1372,70 +1540,88 @@ void CartDebug::addressTypeAsString(ostream& buf, uInt16 addr) const
|
|||
label = myDisLabels[addr & 0xFFF];
|
||||
|
||||
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) << " ";
|
||||
disasmTypeAsString(buf, debugger);
|
||||
AccessTypeAsString(buf, debugger);
|
||||
buf << endl << "tentative: " << Base::toString(label, Base::Fmt::_2_8) << " ";
|
||||
disasmTypeAsString(buf, label);
|
||||
AccessTypeAsString(buf, label);
|
||||
buf << endl;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartDebug::DisasmType CartDebug::disasmTypeAbsolute(uInt8 flags) const
|
||||
Device::AccessType CartDebug::accessTypeAbsolute(Device::AccessFlags flags) const
|
||||
{
|
||||
if(flags & CartDebug::CODE)
|
||||
return CartDebug::CODE;
|
||||
else if(flags & CartDebug::TCODE)
|
||||
return CartDebug::CODE; // TODO - should this be separate??
|
||||
else if(flags & CartDebug::GFX)
|
||||
return CartDebug::GFX;
|
||||
else if(flags & CartDebug::PGFX)
|
||||
return CartDebug::PGFX;
|
||||
else if(flags & CartDebug::DATA)
|
||||
return CartDebug::DATA;
|
||||
else if(flags & CartDebug::ROW)
|
||||
return CartDebug::ROW;
|
||||
if(flags & Device::CODE)
|
||||
return Device::CODE;
|
||||
else if(flags & Device::TCODE)
|
||||
return Device::CODE; // TODO - should this be separate??
|
||||
else if(flags & Device::GFX)
|
||||
return Device::GFX;
|
||||
else if(flags & Device::PGFX)
|
||||
return Device::PGFX;
|
||||
else if(flags & Device::COL)
|
||||
return Device::COL;
|
||||
else if(flags & Device::PCOL)
|
||||
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
|
||||
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)
|
||||
{
|
||||
case CartDebug::CODE: buf << "CODE"; break;
|
||||
case CartDebug::TCODE: buf << "TCODE"; break;
|
||||
case CartDebug::GFX: buf << "GFX"; break;
|
||||
case CartDebug::PGFX: buf << "PGFX"; break;
|
||||
case CartDebug::DATA: buf << "DATA"; break;
|
||||
case CartDebug::ROW: buf << "ROW"; break;
|
||||
case CartDebug::REFERENCED:
|
||||
case CartDebug::VALID_ENTRY:
|
||||
case CartDebug::NONE: break;
|
||||
case Device::CODE: buf << "CODE"; break;
|
||||
case Device::TCODE: buf << "TCODE"; break;
|
||||
case Device::GFX: buf << "GFX"; break;
|
||||
case Device::PGFX: buf << "PGFX"; break;
|
||||
case Device::COL: buf << "COL"; break;
|
||||
case Device::PCOL: buf << "PCOL"; break;
|
||||
case Device::BCOL: buf << "BCOL"; break;
|
||||
case Device::AUD: buf << "AUD"; 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 & CartDebug::CODE)
|
||||
if(flags & Device::CODE)
|
||||
buf << "CODE ";
|
||||
if(flags & CartDebug::TCODE)
|
||||
if(flags & Device::TCODE)
|
||||
buf << "TCODE ";
|
||||
if(flags & CartDebug::GFX)
|
||||
if(flags & Device::GFX)
|
||||
buf << "GFX ";
|
||||
if(flags & CartDebug::PGFX)
|
||||
if(flags & Device::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 ";
|
||||
if(flags & CartDebug::ROW)
|
||||
if(flags & Device::ROW)
|
||||
buf << "ROW ";
|
||||
if(flags & CartDebug::REFERENCED)
|
||||
if(flags & Device::REFERENCED)
|
||||
buf << "*REFERENCED ";
|
||||
if(flags & CartDebug::VALID_ENTRY)
|
||||
if(flags & Device::VALID_ENTRY)
|
||||
buf << "*VALID_ENTRY ";
|
||||
}
|
||||
else
|
||||
|
|
|
@ -31,6 +31,7 @@ using CartMethod = int (CartDebug::*)();
|
|||
|
||||
#include "bspf.hxx"
|
||||
#include "DebuggerSystem.hxx"
|
||||
#include "Device.hxx"
|
||||
|
||||
class CartState : public DebuggerState
|
||||
{
|
||||
|
@ -47,30 +48,8 @@ class CartDebug : public DebuggerSystem
|
|||
friend class DiStella;
|
||||
|
||||
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 {
|
||||
DisasmType type{NONE};
|
||||
Device::AccessType type{Device::NONE};
|
||||
uInt16 address{0};
|
||||
string label;
|
||||
string disasm;
|
||||
|
@ -104,28 +83,55 @@ class CartDebug : public DebuggerSystem
|
|||
CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
|
||||
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
|
||||
int lastReadBaseAddress();
|
||||
// Return the base (= non-mirrored) address of the last CPU write
|
||||
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
|
||||
|
||||
@param force Force a re-disassembly, even if the state hasn't changed
|
||||
|
||||
@return True if disassembly changed from previous call, else false
|
||||
*/
|
||||
bool disassemble(bool force = false);
|
||||
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()
|
||||
*/
|
||||
|
@ -149,7 +155,7 @@ class CartDebug : public DebuggerSystem
|
|||
|
||||
@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
|
||||
|
@ -157,14 +163,14 @@ class CartDebug : public DebuggerSystem
|
|||
things can't be automatically determined. For now, these directives
|
||||
have exactly the same syntax as in a distella configuration file.
|
||||
|
||||
@param type Currently, CODE/DATA/GFX are supported
|
||||
@param 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 end The end address (inclusive) to mark with the given type
|
||||
@param bank Bank to which these directive apply (0 indicated current bank)
|
||||
|
||||
@return True if directive was added, else false if it was removed
|
||||
*/
|
||||
bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end,
|
||||
bool addDirective(Device::AccessType type, uInt16 start, uInt16 end,
|
||||
int bank = -1);
|
||||
|
||||
// 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.
|
||||
*/
|
||||
int bankCount() const;
|
||||
int romBankCount() const;
|
||||
|
||||
/**
|
||||
Add a label and associated address.
|
||||
|
@ -231,6 +237,11 @@ class CartDebug : public DebuggerSystem
|
|||
string saveDisassembly();
|
||||
string saveRom();
|
||||
|
||||
/**
|
||||
Save access counters file
|
||||
*/
|
||||
string saveAccessFile();
|
||||
|
||||
/**
|
||||
Show Distella directives (both set by the user and determined by Distella)
|
||||
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;
|
||||
|
||||
// Convert given address to corresponding disassembly type and append to buf
|
||||
void addressTypeAsString(ostream& buf, uInt16 addr) const;
|
||||
// Convert given address to corresponding access type and append to buf
|
||||
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:
|
||||
using AddrToLabel = std::map<uInt16, string>;
|
||||
using LabelToAddr = std::map<string, uInt16,
|
||||
std::function<bool(const string&, const string&)>>;
|
||||
|
||||
using AddrTypeArray = std::array<uInt8, 0x1000>;
|
||||
using AddrTypeArray = std::array<uInt16, 0x1000>;
|
||||
|
||||
struct DirectiveTag {
|
||||
DisasmType type{NONE};
|
||||
Device::AccessType type{Device::NONE};
|
||||
uInt16 start{0};
|
||||
uInt16 end{0};
|
||||
};
|
||||
|
@ -290,6 +304,18 @@ class CartDebug : public DebuggerSystem
|
|||
};
|
||||
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
|
||||
// Return whether the search address was actually in the list
|
||||
bool fillDisassemblyList(BankInfo& bankinfo, uInt16 search);
|
||||
|
@ -298,15 +324,12 @@ class CartDebug : public DebuggerSystem
|
|||
// based on its disassembly
|
||||
void getBankDirectives(ostream& buf, BankInfo& info) const;
|
||||
|
||||
// Get disassembly enum type from 'flags', taking precendence into account
|
||||
DisasmType disasmTypeAbsolute(uInt8 flags) const;
|
||||
// Get access enum type from 'flags', taking precendence into account
|
||||
Device::AccessType accessTypeAbsolute(Device::AccessFlags flags) const;
|
||||
|
||||
// Convert disassembly enum type to corresponding string and append to buf
|
||||
void disasmTypeAsString(ostream& buf, DisasmType type) const;
|
||||
|
||||
// Convert all disassembly types in 'flags' to corresponding string and
|
||||
// Convert all access types in 'flags' to corresponding string and
|
||||
// append to buf
|
||||
void disasmTypeAsString(ostream& buf, uInt8 flags) const;
|
||||
void AccessTypeAsString(ostream& buf, Device::AccessFlags flags) const;
|
||||
|
||||
private:
|
||||
const OSystem& myOSystem;
|
||||
|
@ -346,9 +369,6 @@ class CartDebug : public DebuggerSystem
|
|||
// The maximum length of all labels currently defined
|
||||
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
|
||||
static std::array<const char*, 16> ourTIAMnemonicR; // read mode
|
||||
static std::array<const char*, 64> ourTIAMnemonicW; // write mode
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "M6502.hxx"
|
||||
#include "System.hxx"
|
||||
#include "Debugger.hxx"
|
||||
#include "CartDebug.hxx"
|
||||
#include "TIADebug.hxx"
|
||||
|
||||
#include "CpuDebug.hxx"
|
||||
|
@ -46,6 +45,7 @@ const DebuggerState& CpuDebug::getState()
|
|||
myState.srcA = my6502.lastSrcAddressA();
|
||||
myState.srcX = my6502.lastSrcAddressX();
|
||||
myState.srcY = my6502.lastSrcAddressY();
|
||||
myState.dest = my6502.lastWriteAddress();
|
||||
|
||||
Debugger::set_bits(myState.PS, myState.PSbits);
|
||||
|
||||
|
@ -66,6 +66,7 @@ void CpuDebug::saveOldState()
|
|||
myOldState.srcA = my6502.lastSrcAddressA();
|
||||
myOldState.srcX = my6502.lastSrcAddressX();
|
||||
myOldState.srcY = my6502.lastSrcAddressY();
|
||||
myOldState.dest = my6502.lastWriteAddress();
|
||||
|
||||
Debugger::set_bits(myOldState.PS, myOldState.PSbits);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class CpuState : public DebuggerState
|
|||
{
|
||||
public:
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -115,7 +115,8 @@ void Debugger::initialize()
|
|||
FBInitStatus Debugger::initializeVideo()
|
||||
{
|
||||
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;
|
||||
|
||||
// autoexec.script is always run
|
||||
FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.script");
|
||||
FilesystemNode autoexec(myOSystem.baseDir().getPath() + "autoexec.script");
|
||||
buf << "autoExec():" << endl
|
||||
<< myParser->exec(autoexec, history) << endl;
|
||||
|
||||
|
@ -292,9 +293,10 @@ void Debugger::loadAllStates()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int Debugger::step()
|
||||
int Debugger::step(bool save)
|
||||
{
|
||||
saveOldState();
|
||||
if(save)
|
||||
saveOldState();
|
||||
|
||||
uInt64 startCycle = mySystem.cycles();
|
||||
|
||||
|
@ -302,7 +304,8 @@ int Debugger::step()
|
|||
myOSystem.console().tia().updateScanlineByStep().flushLineCache();
|
||||
lockSystem();
|
||||
|
||||
addState("step");
|
||||
if(save)
|
||||
addState("step");
|
||||
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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Debugger::poke(uInt16 addr, uInt8 value, uInt8 flags)
|
||||
void Debugger::poke(uInt16 addr, uInt8 value, Device::AccessFlags 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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int Debugger::dpeekAsInt(int addr, uInt8 flags)
|
||||
int Debugger::dpeekAsInt(int addr, Device::AccessFlags flags)
|
||||
{
|
||||
return mySystem.peek(uInt16(addr), flags) |
|
||||
(mySystem.peek(uInt16(addr+1), flags) << 8);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int Debugger::getAccessFlags(uInt16 addr) const
|
||||
Device::AccessFlags Debugger::getAccessFlags(uInt16 addr) const
|
||||
{
|
||||
return mySystem.getAccessFlags(addr);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Debugger::setAccessFlags(uInt16 addr, uInt8 flags)
|
||||
void Debugger::setAccessFlags(uInt16 addr, Device::AccessFlags flags)
|
||||
{
|
||||
mySystem.setAccessFlags(addr, flags);
|
||||
}
|
||||
|
@ -688,6 +691,7 @@ void Debugger::setStartState()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Debugger::setQuitState()
|
||||
{
|
||||
myDialog->saveConfig();
|
||||
saveOldState();
|
||||
|
||||
// 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
|
||||
std::array<Debugger::PseudoRegister, 11> Debugger::ourPseudoRegisters = { {
|
||||
std::array<Debugger::PseudoRegister, 12> Debugger::ourPseudoRegisters = { {
|
||||
// Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = {
|
||||
{ "_bank", "Currently selected bank" },
|
||||
{ "_cclocks", "Color clocks on current scanline" },
|
||||
|
@ -881,12 +885,15 @@ std::array<Debugger::PseudoRegister, 11> Debugger::ourPseudoRegisters = { {
|
|||
{ "_cycleslo", "Lower 32 bits of number of cycles since emulation started" },
|
||||
{ "_fcount", "Number of frames since emulation 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" },
|
||||
{ "_scanend", "Scanline count at end of last frame" },
|
||||
{ "_scycles", "Number of cycles in current scanline" },
|
||||
{ "_vblank", "Whether vertical blank is enabled (1 or 0)" },
|
||||
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" }
|
||||
// CPU address access functions:
|
||||
/*{ "__lastread", "last CPU read address" },
|
||||
{ "__lastwrite", "last CPU write address" },*/
|
||||
/*{ "_lastread", "last CPU read 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 "DebuggerDialog.hxx"
|
||||
#include "FrameBufferConstants.hxx"
|
||||
#include "Cart.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
/**
|
||||
|
@ -243,18 +244,18 @@ class Debugger : public DialogContainer
|
|||
static Debugger& debugger() { return *myStaticDebugger; }
|
||||
|
||||
/** Convenience methods to access peek/poke from System */
|
||||
uInt8 peek(uInt16 addr, uInt8 flags = 0);
|
||||
uInt16 dpeek(uInt16 addr, uInt8 flags = 0);
|
||||
void poke(uInt16 addr, uInt8 value, uInt8 flags = 0);
|
||||
uInt8 peek(uInt16 addr, Device::AccessFlags flags = Device::NONE);
|
||||
uInt16 dpeek(uInt16 addr, Device::AccessFlags flags = Device::NONE);
|
||||
void poke(uInt16 addr, uInt8 value, Device::AccessFlags flags = Device::NONE);
|
||||
|
||||
/** Convenience method to access the 6502 from System */
|
||||
M6502& m6502() const;
|
||||
|
||||
/** These are now exposed so Expressions can use them. */
|
||||
int peekAsInt(int addr, uInt8 flags = 0);
|
||||
int dpeekAsInt(int addr, uInt8 flags = 0);
|
||||
int getAccessFlags(uInt16 addr) const;
|
||||
void setAccessFlags(uInt16 addr, uInt8 flags);
|
||||
int peekAsInt(int addr, Device::AccessFlags flags = Device::NONE);
|
||||
int dpeekAsInt(int addr, Device::AccessFlags flags = Device::NONE);
|
||||
Device::AccessFlags getAccessFlags(uInt16 addr) const;
|
||||
void setAccessFlags(uInt16 addr, Device::AccessFlags flags);
|
||||
|
||||
uInt32 getBaseAddress(uInt32 addr, bool read);
|
||||
|
||||
|
@ -305,7 +306,7 @@ class Debugger : public DialogContainer
|
|||
*/
|
||||
void setQuitState();
|
||||
|
||||
int step();
|
||||
int step(bool save = true);
|
||||
int trace();
|
||||
void nextScanline(int lines);
|
||||
void nextFrame(int frames);
|
||||
|
@ -362,7 +363,7 @@ class Debugger : public DialogContainer
|
|||
string name, help;
|
||||
};
|
||||
static std::array<BuiltinFunction, 18> ourBuiltinFunctions;
|
||||
static std::array<PseudoRegister, 11> ourPseudoRegisters;
|
||||
static std::array<PseudoRegister, 12> ourPseudoRegisters;
|
||||
|
||||
private:
|
||||
// rewind/unwind n states
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "M6502.hxx"
|
||||
#include "Expression.hxx"
|
||||
#include "FSNode.hxx"
|
||||
#include "OSystem.hxx"
|
||||
#include "Settings.hxx"
|
||||
#include "PromptWidget.hxx"
|
||||
#include "RomWidget.hxx"
|
||||
|
@ -111,6 +112,8 @@ string DebuggerParser::run(const string& command)
|
|||
if(validateArgs(i))
|
||||
{
|
||||
myCommand = i;
|
||||
if(commands[i].refreshRequired)
|
||||
debugger.baseDialog()->saveConfig();
|
||||
commands[i].executor(this);
|
||||
}
|
||||
|
||||
|
@ -129,9 +132,9 @@ string DebuggerParser::exec(const FilesystemNode& file, StringList* history)
|
|||
{
|
||||
if(file.exists())
|
||||
{
|
||||
ifstream in(file.getPath());
|
||||
if(!in.is_open())
|
||||
return red("script file \'" + file.getShortPath() + "\' not found");
|
||||
stringstream in;
|
||||
try { file.read(in); }
|
||||
catch(...) { return red("script file \'" + file.getShortPath() + "\' not found"); }
|
||||
|
||||
ostringstream buf;
|
||||
int count = 0;
|
||||
|
@ -630,11 +633,7 @@ string DebuggerParser::saveScriptFile(string file)
|
|||
if(file.find_last_of('.') == string::npos)
|
||||
file += ".script";
|
||||
|
||||
FilesystemNode node(debugger.myOSystem.defaultSaveDir() + file);
|
||||
ofstream out(node.getPath());
|
||||
if(!out.is_open())
|
||||
return "Unable to save script to " + node.getShortPath();
|
||||
|
||||
stringstream out;
|
||||
Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
|
||||
for(const auto& f: funcs)
|
||||
if (!debugger.isBuiltinFunction(f.first))
|
||||
|
@ -675,9 +674,42 @@ string DebuggerParser::saveScriptFile(string file)
|
|||
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";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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.
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -689,6 +721,13 @@ void DebuggerParser::executeA()
|
|||
debugger.cpuDebug().setA(uInt8(args[0]));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "aud"
|
||||
void DebuggerParser::executeAud()
|
||||
{
|
||||
executeDirective(Device::AUD);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "base"
|
||||
void DebuggerParser::executeBase()
|
||||
|
@ -720,13 +759,20 @@ void DebuggerParser::executeBase()
|
|||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "bcol"
|
||||
void DebuggerParser::executeBCol()
|
||||
{
|
||||
executeDirective(Device::BCOL);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "break"
|
||||
void DebuggerParser::executeBreak()
|
||||
{
|
||||
uInt16 addr;
|
||||
uInt8 bank;
|
||||
uInt32 bankCount = debugger.cartDebug().bankCount();
|
||||
uInt32 romBankCount = debugger.cartDebug().romBankCount();
|
||||
|
||||
if(argCount == 0)
|
||||
addr = debugger.cpuDebug().pc();
|
||||
|
@ -738,7 +784,7 @@ void DebuggerParser::executeBreak()
|
|||
else
|
||||
{
|
||||
bank = args[1];
|
||||
if(bank >= bankCount && bank != 0xff)
|
||||
if(bank >= romBankCount && bank != 0xff)
|
||||
{
|
||||
commandResult << red("invalid bank");
|
||||
return;
|
||||
|
@ -754,12 +800,12 @@ void DebuggerParser::executeBreak()
|
|||
commandResult << "cleared";
|
||||
|
||||
commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
|
||||
if(bankCount > 1)
|
||||
if(romBankCount > 1)
|
||||
commandResult << " in bank #" << std::dec << int(bank);
|
||||
}
|
||||
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);
|
||||
|
||||
|
@ -772,7 +818,7 @@ void DebuggerParser::executeBreak()
|
|||
commandResult << "cleared";
|
||||
|
||||
commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
|
||||
if(bankCount > 1)
|
||||
if(romBankCount > 1)
|
||||
commandResult << " in bank #" << std::dec << int(bank);
|
||||
}
|
||||
}
|
||||
|
@ -912,22 +958,14 @@ void DebuggerParser::executeCls()
|
|||
// "code"
|
||||
void DebuggerParser::executeCode()
|
||||
{
|
||||
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;
|
||||
}
|
||||
executeDirective(Device::CODE);
|
||||
}
|
||||
|
||||
bool result = debugger.cartDebug().addDirective(
|
||||
CartDebug::CODE, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " CODE directive on range $"
|
||||
<< hex << args[0] << " $" << hex << args[1];
|
||||
debugger.rom().invalidate();
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "col"
|
||||
void DebuggerParser::executeCol()
|
||||
{
|
||||
executeDirective(Device::COL);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -953,22 +991,7 @@ void DebuggerParser::executeD()
|
|||
// "data"
|
||||
void DebuggerParser::executeData()
|
||||
{
|
||||
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(
|
||||
CartDebug::DATA, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " DATA directive on range $"
|
||||
<< hex << args[0] << " $" << hex << args[1];
|
||||
debugger.rom().invalidate();
|
||||
executeDirective(Device::DATA);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1067,7 +1090,7 @@ void DebuggerParser::executeDisasm()
|
|||
return;
|
||||
}
|
||||
|
||||
commandResult << debugger.cartDebug().disassemble(start, lines);
|
||||
commandResult << debugger.cartDebug().disassembleLines(start, lines);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1123,7 +1146,7 @@ void DebuggerParser::executeDump()
|
|||
file << ".dump";
|
||||
FilesystemNode node(file.str());
|
||||
// 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())
|
||||
{
|
||||
outputCommandError("Unable to append dump to file " + node.getShortPath(), myCommand);
|
||||
|
@ -1204,9 +1227,8 @@ void DebuggerParser::executeExec()
|
|||
if(file.find_last_of('.') == string::npos)
|
||||
file += ".script";
|
||||
FilesystemNode node(file);
|
||||
if (!node.exists()) {
|
||||
node = FilesystemNode(debugger.myOSystem.defaultSaveDir() + file);
|
||||
}
|
||||
if (!node.exists())
|
||||
node = FilesystemNode(debugger.myOSystem.defaultSaveDir().getPath() + file);
|
||||
|
||||
if (argCount == 2) {
|
||||
execPrefix = argStrings[1];
|
||||
|
@ -1270,22 +1292,7 @@ void DebuggerParser::executeFunction()
|
|||
// "gfx"
|
||||
void DebuggerParser::executeGfx()
|
||||
{
|
||||
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(
|
||||
CartDebug::GFX, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " GFX directive on range $"
|
||||
<< hex << args[0] << " $" << hex << args[1];
|
||||
debugger.rom().invalidate();
|
||||
executeDirective(Device::GFX);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1460,11 +1467,11 @@ void DebuggerParser::executeListbreaks()
|
|||
{
|
||||
stringstream buf;
|
||||
int count = 0;
|
||||
uInt32 bankCount = debugger.cartDebug().bankCount();
|
||||
uInt32 romBankCount = debugger.cartDebug().romBankCount();
|
||||
|
||||
for(const auto& bp : debugger.breakPoints().getBreakpoints())
|
||||
{
|
||||
if(bankCount == 1)
|
||||
if(romBankCount == 1)
|
||||
{
|
||||
buf << debugger.cartDebug().getLabel(bp.addr, true, 4) << " ";
|
||||
if(!(++count % 8)) buf << endl;
|
||||
|
@ -1626,26 +1633,18 @@ void DebuggerParser::executePc()
|
|||
debugger.cpuDebug().setPC(args[0]);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "pcol"
|
||||
void DebuggerParser::executePCol()
|
||||
{
|
||||
executeDirective(Device::PCOL);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "pgfx"
|
||||
void DebuggerParser::executePGfx()
|
||||
{
|
||||
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(
|
||||
CartDebug::PGFX, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " PGFX directive on range $"
|
||||
<< hex << args[0] << " $" << hex << args[1];
|
||||
debugger.rom().invalidate();
|
||||
executeDirective(Device::PGFX);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1722,22 +1721,7 @@ void DebuggerParser::executeRom()
|
|||
// "row"
|
||||
void DebuggerParser::executeRow()
|
||||
{
|
||||
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(
|
||||
CartDebug::ROW, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " ROW directive on range $"
|
||||
<< hex << args[0] << " $" << hex << args[1];
|
||||
debugger.rom().invalidate();
|
||||
executeDirective(Device::ROW);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1756,6 +1740,8 @@ void DebuggerParser::executeRunTo()
|
|||
const CartDebug& cartdbg = debugger.cartDebug();
|
||||
const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
|
||||
|
||||
debugger.saveOldState();
|
||||
|
||||
uInt32 count = 0, max_iterations = uInt32(list.size());
|
||||
|
||||
// Create a progress dialog box to show the progress searching through the
|
||||
|
@ -1767,7 +1753,7 @@ void DebuggerParser::executeRunTo()
|
|||
|
||||
bool done = false;
|
||||
do {
|
||||
debugger.step();
|
||||
debugger.step(false);
|
||||
|
||||
// Update romlist to point to current PC
|
||||
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
|
||||
|
@ -1799,24 +1785,36 @@ void DebuggerParser::executeRunToPc()
|
|||
const CartDebug& cartdbg = debugger.cartDebug();
|
||||
const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
|
||||
|
||||
debugger.saveOldState();
|
||||
|
||||
uInt32 count = 0;
|
||||
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 {
|
||||
debugger.step();
|
||||
debugger.step(false);
|
||||
|
||||
// Update romlist to point to current PC
|
||||
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
|
||||
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)
|
||||
commandResult
|
||||
<< "set PC to " << Base::HEX4 << args[0] << " in "
|
||||
<< dec << count << " disassembled instructions";
|
||||
<< "Set PC to $" << Base::HEX4 << args[0] << " in "
|
||||
<< dec << count << " instructions";
|
||||
else
|
||||
commandResult
|
||||
<< "PC " << Base::HEX4 << args[0] << " not reached or found in "
|
||||
<< dec << count << " disassembled instructions";
|
||||
<< "PC $" << Base::HEX4 << args[0] << " not reached or found in "
|
||||
<< dec << count << " instructions";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1833,6 +1831,13 @@ void DebuggerParser::executeSave()
|
|||
commandResult << saveScriptFile(argStrings[0]);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "saveaccess"
|
||||
void DebuggerParser::executeSaveAccess()
|
||||
{
|
||||
commandResult << debugger.cartDebug().saveAccessFile();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "saveconfig"
|
||||
void DebuggerParser::executeSaveconfig()
|
||||
|
@ -1947,9 +1952,23 @@ void DebuggerParser::executeStepwhile()
|
|||
}
|
||||
Expression* expr = YaccParser::getResult();
|
||||
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 {
|
||||
ncycles += debugger.step();
|
||||
} while (expr->evaluate());
|
||||
ncycles += debugger.step(false);
|
||||
|
||||
progress.setProgress(count);
|
||||
} while (expr->evaluate() && ++count < max_iterations);
|
||||
|
||||
progress.close();
|
||||
commandResult << "executed " << ncycles << " cycles";
|
||||
}
|
||||
|
||||
|
@ -2054,18 +2073,18 @@ void DebuggerParser::executeTraps(bool read, bool write, const string& command,
|
|||
if(read)
|
||||
{
|
||||
if(beginRead != endRead)
|
||||
conditionBuf << "__lastread>=" << Base::toString(beginRead) << "&&__lastread<=" << Base::toString(endRead);
|
||||
conditionBuf << "__lastbaseread>=" << Base::toString(beginRead) << "&&__lastbaseread<=" << Base::toString(endRead);
|
||||
else
|
||||
conditionBuf << "__lastread==" << Base::toString(beginRead);
|
||||
conditionBuf << "__lastbaseread==" << Base::toString(beginRead);
|
||||
}
|
||||
if(read && write)
|
||||
conditionBuf << "||";
|
||||
if(write)
|
||||
{
|
||||
if(beginWrite != endWrite)
|
||||
conditionBuf << "__lastwrite>=" << Base::toString(beginWrite) << "&&__lastwrite<=" << Base::toString(endWrite);
|
||||
conditionBuf << "__lastbasewrite>=" << Base::toString(beginWrite) << "&&__lastbasewrite<=" << Base::toString(endWrite);
|
||||
else
|
||||
conditionBuf << "__lastwrite==" << Base::toString(beginWrite);
|
||||
conditionBuf << "__lastbasewrite==" << Base::toString(beginWrite);
|
||||
}
|
||||
// parenthesize provided condition (end)
|
||||
if(hasCond)
|
||||
|
@ -2194,7 +2213,7 @@ void DebuggerParser::executeType()
|
|||
for(uInt32 i = beg; i <= end; ++i)
|
||||
{
|
||||
commandResult << Base::HEX4 << i << ": ";
|
||||
debugger.cartDebug().addressTypeAsString(commandResult, i);
|
||||
debugger.cartDebug().accessTypeAsString(commandResult, i);
|
||||
commandResult << endl;
|
||||
}
|
||||
}
|
||||
|
@ -2300,7 +2319,7 @@ void DebuggerParser::executeZ()
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// List of all commands available to the parser
|
||||
std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
||||
std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
|
||||
{
|
||||
"a",
|
||||
"Set Accumulator to <value>",
|
||||
|
@ -2311,6 +2330,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
|||
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",
|
||||
"Set default number base to <base>",
|
||||
|
@ -2321,6 +2350,17 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
|||
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 at <address> and <bank>",
|
||||
|
@ -2442,6 +2482,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
|||
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",
|
||||
"Show value xx as TIA color",
|
||||
|
@ -2847,6 +2897,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
|||
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",
|
||||
"Mark 'PGFX' range in disassembly",
|
||||
|
@ -2980,6 +3040,16 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
|||
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",
|
||||
"Save Distella config file (with default name)",
|
||||
|
@ -3182,7 +3252,7 @@ std::array<DebuggerParser::Command, 95> DebuggerParser::commands = { {
|
|||
|
||||
{
|
||||
"type",
|
||||
"Show disassembly type for address xx [yy]",
|
||||
"Show access type for address xx [yy]",
|
||||
"Example: type f000, type f000 f010",
|
||||
true,
|
||||
false,
|
||||
|
|
|
@ -27,6 +27,7 @@ class FilesystemNode;
|
|||
struct Command;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Device.hxx"
|
||||
|
||||
class DebuggerParser
|
||||
{
|
||||
|
@ -97,7 +98,7 @@ class DebuggerParser
|
|||
std::array<Parameters, 10> parms;
|
||||
std::function<void (DebuggerParser*)> executor;
|
||||
};
|
||||
static std::array<Command, 95> commands;
|
||||
static std::array<Command, 100> commands;
|
||||
|
||||
struct Trap
|
||||
{
|
||||
|
@ -140,9 +141,13 @@ class DebuggerParser
|
|||
// output the error with the example provided for the command
|
||||
void outputCommandError(const string& errorMsg, int command);
|
||||
|
||||
void executeDirective(Device::AccessType type);
|
||||
|
||||
// List of available command methods
|
||||
void executeA();
|
||||
void executeAud();
|
||||
void executeBase();
|
||||
void executeBCol();
|
||||
void executeBreak();
|
||||
void executeBreakif();
|
||||
void executeBreaklabel();
|
||||
|
@ -155,6 +160,7 @@ class DebuggerParser
|
|||
void executeClearwatches();
|
||||
void executeCls();
|
||||
void executeCode();
|
||||
void executeCol();
|
||||
void executeColortest();
|
||||
void executeD();
|
||||
void executeData();
|
||||
|
@ -195,6 +201,7 @@ class DebuggerParser
|
|||
void executeN();
|
||||
void executePalette();
|
||||
void executePc();
|
||||
void executePCol();
|
||||
void executePGfx();
|
||||
void executePrint();
|
||||
void executeRam();
|
||||
|
@ -208,6 +215,7 @@ class DebuggerParser
|
|||
void executeRunToPc();
|
||||
void executeS();
|
||||
void executeSave();
|
||||
void executeSaveAccess();
|
||||
void executeSaveallstates();
|
||||
void executeSaveconfig();
|
||||
void executeSavedisassembly();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "Base.hxx"
|
||||
#include "CartDebug.hxx"
|
||||
#include "Device.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
/**
|
||||
|
@ -71,11 +72,26 @@ class DiStella
|
|||
CartDebug::AddrTypeArray& directives,
|
||||
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:
|
||||
// Indicate that a new line of disassembly has been completed
|
||||
// In the original Distella code, this indicated a new line to be printed
|
||||
// Here, we add a new entry to the DisassemblyList
|
||||
void addEntry(CartDebug::DisasmType type);
|
||||
void addEntry(Device::AccessType type);
|
||||
|
||||
// Process directives given in the list
|
||||
// Directives are basically the contents of a distella configuration file
|
||||
|
@ -87,32 +103,32 @@ class DiStella
|
|||
void disasmFromAddress(uInt32 distart);
|
||||
|
||||
bool check_range(uInt16 start, uInt16 end) const;
|
||||
int mark(uInt32 address, uInt8 mask, bool directive = false);
|
||||
bool checkBit(uInt16 address, uInt8 mask, bool useDebugger = true) const;
|
||||
|
||||
bool checkBits(uInt16 address, uInt8 mask, uInt8 notMask, bool useDebugger = true) const;
|
||||
AddressType mark(uInt32 address, uInt16 mask, bool directive = false);
|
||||
bool checkBit(uInt16 address, uInt16 mask, bool useDebugger = true) const;
|
||||
bool checkBits(uInt16 address, uInt16 mask, uInt16 notMask, bool useDebugger = true) const;
|
||||
void outputGraphics();
|
||||
void outputBytes(CartDebug::DisasmType type);
|
||||
void outputColors();
|
||||
void outputBytes(Device::AccessType type);
|
||||
|
||||
// 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))
|
||||
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);
|
||||
if (labfound == 2)
|
||||
if (labfound == AddressType::TIA)
|
||||
{
|
||||
if(ourLookup[op].rw_mode == RWMode::READ)
|
||||
myReserved.TIARead[addr & 0x0F] = true;
|
||||
else
|
||||
myReserved.TIAWrite[addr & 0x3F] = true;
|
||||
}
|
||||
else if (labfound == 3)
|
||||
else if (labfound == AddressType::RIOT)
|
||||
myReserved.IOReadWrite[(addr & 0xFF) - 0x80] = true;
|
||||
else if (labfound == 5)
|
||||
else if (labfound == AddressType::ZP_RAM)
|
||||
myReserved.ZPRAM[(addr & 0xFF) - 0x80] = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,80 +16,25 @@
|
|||
//============================================================================
|
||||
|
||||
#include "Cart0840.hxx"
|
||||
#include "PopUpWidget.hxx"
|
||||
#include "Cart0840Widget.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Cartridge0840Widget::Cartridge0840Widget(
|
||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||
int x, int y, int w, int h, Cartridge0840& cart)
|
||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
||||
myCart(cart)
|
||||
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||
{
|
||||
uInt16 size = 2 * 4096;
|
||||
myHotspotDelta = 0x40;
|
||||
initialize();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Cartridge0840Widget::description()
|
||||
{
|
||||
ostringstream info;
|
||||
info << "0840 ECONObanking, two 4K banks\n"
|
||||
<< "Startup bank = " << cart.startBank() << "\n";
|
||||
|
||||
// Eventually, we should query this from the debugger/disassembler
|
||||
for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2;
|
||||
++i, offset += 0x1000, spot += 0x40)
|
||||
{
|
||||
uInt16 start = uInt16((cart.myImage[offset+1] << 8) | cart.myImage[offset]);
|
||||
start -= start % 0x1000;
|
||||
info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - "
|
||||
<< "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n";
|
||||
}
|
||||
info << "0840 ECONObanking, two 4K banks\n";
|
||||
info << CartridgeEnhancedWidget::description();
|
||||
|
||||
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();
|
||||
return info.str();
|
||||
}
|
||||
|
|
|
@ -19,11 +19,10 @@
|
|||
#define CARTRIDGE0840_WIDGET_HXX
|
||||
|
||||
class Cartridge0840;
|
||||
class PopUpWidget;
|
||||
|
||||
#include "CartDebugWidget.hxx"
|
||||
#include "CartEnhancedWidget.hxx"
|
||||
|
||||
class Cartridge0840Widget : public CartDebugWidget
|
||||
class Cartridge0840Widget : public CartridgeEnhancedWidget
|
||||
{
|
||||
public:
|
||||
Cartridge0840Widget(GuiObject* boss, const GUI::Font& lfont,
|
||||
|
@ -33,17 +32,11 @@ class Cartridge0840Widget : public CartDebugWidget
|
|||
virtual ~Cartridge0840Widget() = default;
|
||||
|
||||
private:
|
||||
Cartridge0840& myCart;
|
||||
PopUpWidget* myBank{nullptr};
|
||||
string manufacturer() override { return "Fred X. Quimby"; }
|
||||
|
||||
enum { kBankChanged = 'bkCH' };
|
||||
string description() override;
|
||||
|
||||
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
|
||||
Cartridge0840Widget() = delete;
|
||||
Cartridge0840Widget(const Cartridge0840Widget&) = delete;
|
||||
|
|
|
@ -22,15 +22,18 @@
|
|||
Cartridge2KWidget::Cartridge2KWidget(
|
||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||
int x, int y, int w, int h, Cartridge2K& cart)
|
||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h)
|
||||
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||
{
|
||||
// Eventually, we should query this from the debugger/disassembler
|
||||
size_t size = cart.mySize;
|
||||
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
|
||||
start -= start % size;
|
||||
|
||||
ostringstream info;
|
||||
info << "Standard 2K cartridge, non-bankswitched\n"
|
||||
<< "Accessible @ $" << Common::Base::HEX4 << start << " - " << "$" << (start + size - 1);
|
||||
addBaseInformation(size, "Atari", info.str());
|
||||
initialize();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Cartridge2KWidget::description()
|
||||
{
|
||||
ostringstream info;
|
||||
|
||||
info << "Standard 2K cartridge, non-bankswitched\n";
|
||||
info << CartridgeEnhancedWidget::description();
|
||||
|
||||
return info.str();
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
|
||||
class Cartridge2K;
|
||||
|
||||
#include "CartDebugWidget.hxx"
|
||||
#include "CartEnhancedWidget.hxx"
|
||||
|
||||
class Cartridge2KWidget : public CartDebugWidget
|
||||
class Cartridge2KWidget : public CartridgeEnhancedWidget
|
||||
{
|
||||
public:
|
||||
Cartridge2KWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||
|
@ -32,10 +32,11 @@ class Cartridge2KWidget : public CartDebugWidget
|
|||
virtual ~Cartridge2KWidget() = default;
|
||||
|
||||
private:
|
||||
// No implementation for non-bankswitched ROMs
|
||||
void loadConfig() override { }
|
||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override { }
|
||||
string manufacturer() override { return "Atari"; }
|
||||
|
||||
string description() override;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
Cartridge2KWidget() = delete;
|
||||
Cartridge2KWidget(const Cartridge2KWidget&) = delete;
|
||||
|
|
|
@ -18,320 +18,230 @@
|
|||
#include "Cart3EPlus.hxx"
|
||||
#include "EditTextWidget.hxx"
|
||||
#include "PopUpWidget.hxx"
|
||||
#include "Cart3EPlusWidget.hxx"
|
||||
#include "CartEnhancedWidget.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Cartridge3EPlusWidget::Cartridge3EPlusWidget(
|
||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||
int x, int y, int w, int h, Cartridge3EPlus& cart)
|
||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
||||
myCart(cart)
|
||||
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart),
|
||||
myCart3EP(cart)
|
||||
{
|
||||
size_t size = cart.mySize;
|
||||
|
||||
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;
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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)
|
||||
myOldState.internalram.push_back(myCart.myRAM[i]);
|
||||
info << "3E+ cartridge - (4" << ELLIPSIS << "64K ROM + RAM)\n"
|
||||
<< " " << 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()
|
||||
{
|
||||
CartridgeEnhancedWidget::loadConfig();
|
||||
updateUIState();
|
||||
CartDebugWidget::loadConfig();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Cartridge3EPlusWidget::handleCommand(CommandSender* sender,
|
||||
int cmd, int data, int id)
|
||||
{
|
||||
uInt16 segment = 0;
|
||||
const uInt16 segment = id;
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
case kBank0Changed:
|
||||
segment = 0;
|
||||
case kBankChanged:
|
||||
case kRomRamChanged:
|
||||
{
|
||||
const bool isROM = myBankType[segment]->getSelectedTag() == "ROM";
|
||||
int bank = myBankWidgets[segment]->getSelected();
|
||||
|
||||
myBankCommit[segment]->setEnabled((isROM && bank < myCart.romBankCount())
|
||||
|| (!isROM && bank < myCart.ramBankCount()));
|
||||
break;
|
||||
case kBank1Changed:
|
||||
segment = 1;
|
||||
break;
|
||||
case kBank2Changed:
|
||||
segment = 2;
|
||||
break;
|
||||
case kBank3Changed:
|
||||
segment = 3;
|
||||
}
|
||||
case kChangeBank:
|
||||
{
|
||||
// Ignore bank if either number or type hasn't been selected
|
||||
if(myBankWidgets[segment]->getSelected() < 0 ||
|
||||
myBankType[segment]->getSelected() < 0)
|
||||
return;
|
||||
|
||||
uInt8 bank = myBankWidgets[segment]->getSelected();
|
||||
|
||||
myCart.unlockBank();
|
||||
|
||||
if(myBankType[segment]->getSelectedTag() == "ROM")
|
||||
myCart.bank(bank, segment);
|
||||
else
|
||||
myCart.bank(bank + myCart.romBankCount(), segment);
|
||||
|
||||
myCart.lockBank();
|
||||
invalidate();
|
||||
updateUIState();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore bank if either number or type hasn't been selected
|
||||
if(myBankNumber[segment]->getSelected() < 0 ||
|
||||
myBankType[segment]->getSelected() < 0)
|
||||
return;
|
||||
|
||||
uInt8 bank = (segment << myCart.BANK_BITS) |
|
||||
(myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK);
|
||||
|
||||
myCart.unlockBank();
|
||||
|
||||
if(myBankType[segment]->getSelectedTag() == "ROM")
|
||||
myCart.bankROM(bank);
|
||||
else
|
||||
myCart.bankRAM(bank);
|
||||
|
||||
myCart.lockBank();
|
||||
invalidate();
|
||||
updateUIState();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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()
|
||||
{
|
||||
// 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)
|
||||
for(int i = 0; i < 8; ++i)
|
||||
for(int seg = 0; seg < myCart3EP.myBankSegs; ++seg)
|
||||
{
|
||||
uInt16 bank = myCart.bankInUse[i];
|
||||
uInt16 bank = myCart.getSegmentBank(seg);
|
||||
ostringstream buf;
|
||||
|
||||
if(bank == myCart.BANK_UNDEFINED) // never accessed
|
||||
if(bank >= myCart.romBankCount()) // was RAM mapped here?
|
||||
{
|
||||
myBankState[i]->setText("Undefined");
|
||||
if(i % 2 == 0)
|
||||
{
|
||||
myBankNumber[i/2]->clearSelection();
|
||||
myBankType[i/2]->clearSelection();
|
||||
}
|
||||
uInt16 ramBank = bank - myCart.romBankCount();
|
||||
|
||||
buf << "RAM @ $" << Common::Base::HEX4
|
||||
<< (ramBank << myCart3EP.myBankShift) << " (R)";
|
||||
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
|
||||
{
|
||||
ostringstream buf;
|
||||
int bankno = bank & myCart.BIT_BANK_MASK;
|
||||
buf << "ROM @ $" << Common::Base::HEX4
|
||||
<< ((bank << myCart3EP.myBankShift));
|
||||
myBankState[seg * 2]->setText(buf.str());
|
||||
|
||||
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
|
||||
{
|
||||
if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port
|
||||
{
|
||||
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
|
||||
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (W)";
|
||||
myBankState[i]->setText(buf.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
|
||||
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (R)";
|
||||
myBankState[i]->setText(buf.str());
|
||||
}
|
||||
buf.str("");
|
||||
buf << "ROM @ $" << Common::Base::HEX4
|
||||
<< ((bank << myCart3EP.myBankShift) + myCart3EP.myBankSize);
|
||||
myBankState[seg * 2 + 1]->setText(buf.str());
|
||||
|
||||
if(i % 2 == 0)
|
||||
{
|
||||
myBankNumber[i/2]->setSelected(bankno);
|
||||
myBankType[i/2]->setSelected("RAM");
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
myBankNumber[i/2]->setSelected(bankno);
|
||||
myBankType[i/2]->setSelected("ROM");
|
||||
}
|
||||
}
|
||||
myBankWidgets[seg]->setSelectedIndex(bank);
|
||||
myBankType[seg]->setSelected("ROM");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 Cartridge3EPlusWidget::internalRamSize()
|
||||
{
|
||||
return 32*1024;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 Cartridge3EPlusWidget::internalRamRPort(int start)
|
||||
{
|
||||
return 0x0000 + start;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Cartridge3EPlusWidget::internalRamDescription()
|
||||
{
|
||||
ostringstream desc;
|
||||
|
||||
desc << "Accessible 512b at a time via:\n"
|
||||
<< " $f000/$f400/$f800/$fc00 for Read Access\n"
|
||||
<< " $f200/$f600/$fa00/$fe00 for Write Access (+$200)";
|
||||
<< " $f000/$f400/$f800/$fc00 for read access\n"
|
||||
<< " $f200/$f600/$fa00/$fe00 for write access";
|
||||
|
||||
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 PopUpWidget;
|
||||
|
||||
#include "CartDebugWidget.hxx"
|
||||
#include "CartEnhancedWidget.hxx"
|
||||
|
||||
class Cartridge3EPlusWidget : public CartDebugWidget
|
||||
class Cartridge3EPlusWidget : public CartridgeEnhancedWidget
|
||||
{
|
||||
public:
|
||||
Cartridge3EPlusWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||
|
@ -35,46 +35,33 @@ class Cartridge3EPlusWidget : public CartDebugWidget
|
|||
virtual ~Cartridge3EPlusWidget() = default;
|
||||
|
||||
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();
|
||||
|
||||
private:
|
||||
Cartridge3EPlus& myCart;
|
||||
void loadConfig() override;
|
||||
|
||||
string internalRamDescription() override;
|
||||
|
||||
private:
|
||||
Cartridge3EPlus& myCart3EP;
|
||||
|
||||
std::array<PopUpWidget*, 4> myBankNumber{nullptr};
|
||||
std::array<PopUpWidget*, 4> myBankType{nullptr};
|
||||
std::array<ButtonWidget*, 4> myBankCommit{nullptr};
|
||||
std::array<EditTextWidget*, 8> myBankState{nullptr};
|
||||
|
||||
struct CartState {
|
||||
ByteArray internalram;
|
||||
enum {
|
||||
kRomRamChanged = 'rrCh',
|
||||
kChangeBank = 'chBk',
|
||||
};
|
||||
CartState myOldState;
|
||||
|
||||
enum BankID {
|
||||
kBank0Changed = 'b0CH',
|
||||
kBank1Changed = 'b1CH',
|
||||
kBank2Changed = 'b2CH',
|
||||
kBank3Changed = 'b3CH'
|
||||
};
|
||||
static const std::array<BankID, 4> bankEnum;
|
||||
|
||||
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
|
||||
Cartridge3EPlusWidget() = delete;
|
||||
Cartridge3EPlusWidget(const Cartridge3EPlusWidget&) = delete;
|
||||
|
|
|
@ -23,129 +23,134 @@
|
|||
Cartridge3EWidget::Cartridge3EWidget(
|
||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||
int x, int y, int w, int h, Cartridge3E& cart)
|
||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
||||
myCart(cart),
|
||||
myNumRomBanks(uInt32(cart.mySize >> 11)),
|
||||
myNumRamBanks(32)
|
||||
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||
{
|
||||
size_t size = cart.mySize;
|
||||
|
||||
ostringstream info;
|
||||
info << "3E cartridge - (3F + RAM)\n"
|
||||
<< " 2-256 2K ROM (currently " << myNumRomBanks << "), 32 1K RAM\n"
|
||||
<< "First 2K (ROM) selected by writing to $3F\n"
|
||||
"First 2K (RAM) selected by writing to $3E\n"
|
||||
" $F000 - $F3FF (R), $F400 - $F7FF (W)\n"
|
||||
"Last 2K always points to last 2K of ROM\n";
|
||||
if(cart.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);
|
||||
initialize();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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()
|
||||
{
|
||||
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);
|
||||
myRAMBank->setSelectedMax(myOldState.bank >= 256);
|
||||
myBankWidgets[0]->setSelectedIndex(bank, oldBank != bank);
|
||||
myBankWidgets[1]->setSelectedMax(oldBank >= myCart.romBankCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
myROMBank->setSelectedMax(myOldState.bank < 256);
|
||||
myRAMBank->setSelectedIndex(myCart.myCurrentBank - 256, myOldState.bank != myCart.myCurrentBank);
|
||||
myBankWidgets[0]->setSelectedMax(oldBank < myCart.romBankCount());
|
||||
myBankWidgets[1]->setSelectedIndex(bank - myCart.romBankCount(), oldBank != bank);
|
||||
}
|
||||
|
||||
CartDebugWidget::loadConfig();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Cartridge3EWidget::handleCommand(CommandSender* sender,
|
||||
int cmd, int data, int id)
|
||||
void Cartridge3EWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
|
||||
{
|
||||
uInt16 bank = 0;
|
||||
|
||||
if(cmd == kROMBankChanged)
|
||||
if(cmd == kBankChanged)
|
||||
{
|
||||
if(myROMBank->getSelected() < int(myNumRomBanks))
|
||||
if(myBankWidgets[0]->getSelected() < myCart.romBankCount())
|
||||
{
|
||||
bank = myROMBank->getSelected();
|
||||
myRAMBank->setSelectedMax();
|
||||
bank = myBankWidgets[0]->getSelected();
|
||||
myBankWidgets[1]->setSelectedMax();
|
||||
}
|
||||
else
|
||||
{
|
||||
bank = 256; // default to first RAM bank
|
||||
myRAMBank->setSelectedIndex(0);
|
||||
bank = myCart.romBankCount(); // default to first RAM bank
|
||||
myBankWidgets[1]->setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
else if(cmd == kRAMBankChanged)
|
||||
{
|
||||
if(myRAMBank->getSelected() < int(myNumRamBanks))
|
||||
if(myBankWidgets[1]->getSelected() < myCart.ramBankCount())
|
||||
{
|
||||
myROMBank->setSelectedMax();
|
||||
bank = myRAMBank->getSelected() + 256;
|
||||
myBankWidgets[0]->setSelectedMax();
|
||||
bank = myBankWidgets[1]->getSelected() + myCart.romBankCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
bank = 0; // default to first ROM bank
|
||||
myROMBank->setSelectedIndex(0);
|
||||
myBankWidgets[0]->setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
myCart.unlockBank();
|
||||
myCart.bank(bank);
|
||||
myCart.lockBank();
|
||||
|
@ -156,65 +161,13 @@ void Cartridge3EWidget::handleCommand(CommandSender* sender,
|
|||
string Cartridge3EWidget::bankState()
|
||||
{
|
||||
ostringstream& buf = buffer();
|
||||
uInt16 bank = myCart.getBank();
|
||||
|
||||
uInt16& bank = myCart.myCurrentBank;
|
||||
if(bank < 256)
|
||||
buf << "ROM bank #" << std::dec << bank % myNumRomBanks << ", RAM inactive";
|
||||
if(bank < myCart.romBankCount())
|
||||
buf << "ROM bank #" << std::dec << bank % myCart.romBankCount() << ", RAM inactive";
|
||||
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();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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
|
||||
|
||||
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:
|
||||
Cartridge3EWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||
|
@ -33,39 +34,28 @@ class Cartridge3EWidget : public CartDebugWidget
|
|||
virtual ~Cartridge3EWidget() = default;
|
||||
|
||||
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 {
|
||||
kROMBankChanged = 'rmCH',
|
||||
kRAMBankChanged = 'raCH'
|
||||
};
|
||||
|
||||
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 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
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
Cartridge3EWidget() = delete;
|
||||
Cartridge3EWidget(const Cartridge3EWidget&) = delete;
|
||||
|
|
|
@ -16,79 +16,33 @@
|
|||
//============================================================================
|
||||
|
||||
#include "Cart3F.hxx"
|
||||
#include "PopUpWidget.hxx"
|
||||
#include "Cart3FWidget.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Cartridge3FWidget::Cartridge3FWidget(
|
||||
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
||||
int x, int y, int w, int h, Cartridge3F& cart)
|
||||
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
|
||||
myCart(cart)
|
||||
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
|
||||
{
|
||||
size_t size = cart.mySize;
|
||||
myHotspotDelta = 0;
|
||||
initialize();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Cartridge3FWidget::description()
|
||||
{
|
||||
ostringstream info;
|
||||
info << "Tigervision 3F cartridge, 2-256 2K banks\n"
|
||||
<< "Startup bank = " << cart.startBank() << " or undetermined\n"
|
||||
<< "First 2K bank selected by writing to $3F\n"
|
||||
<< "Last 2K always points to last 2K of ROM\n";
|
||||
size_t size;
|
||||
const ByteBuffer& image = myCart.getImage(size);
|
||||
|
||||
info << "Tigervision 3F cartridge, 2 - 256 2K banks\n"
|
||||
<< "First 2K bank selected by writing to " << hotspotStr() << "\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
|
||||
uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
|
||||
uInt16 start = (image[size-3] << 8) | image[size-4];
|
||||
start -= start % 0x1000;
|
||||
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
|
||||
info << "Bank RORG $" << Common::Base::HEX4 << start << "\n";
|
||||
|
||||
int xpos = 2,
|
||||
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();
|
||||
return info.str();
|
||||
}
|
||||
|
|