commit remaining files

This commit is contained in:
twinaphex committed 2019-03-01 02:33:56 +01:00
1 parent c5b6745226
commit 196a16b031
354 files changed
+208999

No files matched your search

+2652
View File
File diff suppressed because it is too large. Load diff
+237
View File
@@ -0,0 +1,237 @@
;;
;; Supermodel Configuration File
;; Default settings for Version 0.3a.
;;
;
; Quick Overview
; --------------
;
; All settings are case sensitive. Numbers must be integers. Check your
; spelling carefully because invalid settings are silently ignored. To verify
; that your settings are being parsed correctly, check the contents of
; error.log.
;
; Global options apply to all games. To create configuration profiles for
; individual games, place settings under sections with the same name as the
; corresponding MAME ROM set, like so:
;
; ; Scud Race
; [ scud ]
;
; SoundVolume = 50
; MusicVolume = 200
; ; ... etc. ...
;
; Input mappings are an exceptional case: they are only allowed in the global
; section. Per-game input mappings are not supported.
;
; For a list of all valid settings, please consult README.txt. Only default
; inputs are assigned here.
;
[ Global ] ; Input settings can only be read from the global section!
; Network board - experimental build for win32 only
EmulateNet = 0
port_in = 1970
port_out = 1971
addr_out = "127.0.0.1"
; Common
InputStart1 = "KEY_1,JOY1_BUTTON9"
InputStart2 = "KEY_2,JOY2_BUTTON9"
InputCoin1 = "KEY_3,JOY1_BUTTON10"
InputCoin2 = "KEY_4,JOY2_BUTTON10"
InputServiceA = "KEY_5"
InputServiceB = "KEY_7"
InputTestA = "KEY_6"
InputTestB = "KEY_8"
; 4-way digital joysticks
InputJoyUp = "KEY_UP,JOY1_UP"
InputJoyDown = "KEY_DOWN,JOY1_DOWN"
InputJoyLeft = "KEY_LEFT,JOY1_LEFT"
InputJoyRight = "KEY_RIGHT,JOY1_RIGHT"
InputJoyUp2 = "JOY2_UP"
InputJoyDown2 = "JOY2_DOWN"
InputJoyLeft2 = "JOY2_LEFT"
InputJoyRight2 = "JOY2_RIGHT"
; Fighting game buttons
InputPunch = "KEY_A,JOY1_BUTTON1"
InputKick = "KEY_S,JOY1_BUTTON2"
InputGuard = "KEY_D,JOY1_BUTTON3"
InputEscape = "KEY_F,JOY1_BUTTON4"
InputPunch2 = "JOY2_BUTTON1"
InputKick2 = "JOY2_BUTTON2"
InputGuard2 = "JOY2_BUTTON3"
InputEscape2 = "JOY2_BUTTON4"
; Spikeout buttons
InputShift = "KEY_A,JOY1_BUTTON1"
InputBeat = "KEY_S,JOY1_BUTTON2"
InputCharge = "KEY_D,JOY1_BUTTON3"
InputJump = "KEY_F,JOY1_BUTTON4"
; Virtua Striker buttons
InputShortPass = "KEY_A,JOY1_BUTTON1"
InputLongPass = "KEY_S,JOY1_BUTTON2"
InputShoot = "KEY_D,JOY1_BUTTON3"
InputShortPass2 = "JOY2_BUTTON1"
InputLongPass2 = "JOY2_BUTTON2"
InputShoot2 = "JOY2_BUTTON3"
; Steering wheel
InputSteeringLeft = "KEY_LEFT" ; digital, turn wheel left
InputSteeringRight = "KEY_RIGHT" ; digital, turn wheel right
InputSteering = "JOY1_XAXIS" ; analog, full steering range
; Pedals
InputAccelerator = "KEY_UP,JOY1_UP"
InputBrake = "KEY_DOWN,JOY1_DOWN"
; Up/down shifter manual transmission (all racers)
InputGearShiftUp = "KEY_Y" ; sequential shift up
InputGearShiftDown = "KEY_H" ; sequential shift down
; 4-Speed manual transmission (Daytona 2, Sega Rally 2, Scud Race)
InputGearShift1 = "KEY_Q,JOY1_BUTTON5"
InputGearShift2 = "KEY_W,JOY1_BUTTON6"
InputGearShift3 = "KEY_E,JOY1_BUTTON7"
InputGearShift4 = "KEY_R,JOY1_BUTTON8"
InputGearShiftN = "KEY_T"
; VR4 view change buttons (Daytona 2, Le Mans 24, Scud Race)
InputVR1 = "KEY_A,JOY1_BUTTON1"
InputVR2 = "KEY_S,JOY1_BUTTON2"
InputVR3 = "KEY_D,JOY1_BUTTON3"
InputVR4 = "KEY_F,JOY1_BUTTON4"
; Single view change button (Dirt Devils, ECA, Harley-Davidson, Sega Rally 2)
InputViewChange = "KEY_A,JOY1_BUTTON1"
; Handbrake (Dirt Devils, Sega Rally 2)
InputHandBrake = "KEY_S,JOY1_BUTTON2"
; Harley-Davidson controls
InputRearBrake = "KEY_S,JOY1_BUTTON2"
InputMusicSelect = "KEY_D,JOY1_BUTTON3"
; Virtual On macros
InputTwinJoyTurnLeft = "KEY_Q,JOY1_RXAXIS_NEG"
InputTwinJoyTurnRight = "KEY_W,JOY1_RXAXIS_POS"
InputTwinJoyForward = "KEY_UP,JOY1_YAXIS_NEG"
InputTwinJoyReverse = "KEY_DOWN,JOY1_YAXIS_POS"
InputTwinJoyStrafeLeft = "KEY_LEFT,JOY1_XAXIS_NEG"
InputTwinJoyStrafeRight = "KEY_RIGHT,JOY1_XAXIS_POS"
InputTwinJoyJump = "KEY_E,JOY1_BUTTON1"
InputTwinJoyCrouch = "KEY_R,JOY1_BUTTON2"
; Virtual On individual joystick mapping
InputTwinJoyLeft1 = "NONE"
InputTwinJoyLeft2 = "NONE"
InputTwinJoyRight1 = "NONE"
InputTwinJoyRight2 = "NONE"
InputTwinJoyUp1 = "NONE"
InputTwinJoyUp2 = "NONE"
InputTwinJoyDown1 = "NONE"
InputTwinJoyDown2 = "NONE"
; Virtual On buttons
InputTwinJoyShot1 = "KEY_A,JOY1_BUTTON5"
InputTwinJoyShot2 = "KEY_S,JOY1_BUTTON6"
InputTwinJoyTurbo1 = "KEY_Z,JOY1_BUTTON7"
InputTwinJoyTurbo2 = "KEY_X,JOY1_BUTTON8"
; Analog joystick (Star Wars Trilogy)
InputAnalogJoyLeft = "KEY_LEFT" ; digital, move left
InputAnalogJoyRight = "KEY_RIGHT" ; digital, move right
InputAnalogJoyUp = "KEY_UP" ; digital, move up
InputAnalogJoyDown = "KEY_DOWN" ; digital, move down
InputAnalogJoyX = "JOY_XAXIS,MOUSE_XAXIS" ; analog, full X axis
InputAnalogJoyY = "JOY_YAXIS,MOUSE_YAXIS" ; analog, full Y axis
InputAnalogJoyTrigger = "KEY_A,JOY_BUTTON1,MOUSE_LEFT_BUTTON"
InputAnalogJoyEvent = "KEY_S,JOY_BUTTON2,MOUSE_RIGHT_BUTTON"
InputAnalogJoyTrigger2 = "KEY_D,JOY_BUTTON2"
InputAnalogJoyEvent2 = "NONE"
; Light guns (Lost World)
InputGunLeft = "KEY_LEFT" ; digital, move gun left
InputGunRight = "KEY_RIGHT" ; digital, move gun right
InputGunUp = "KEY_UP" ; digital, move gun up
InputGunDown = "KEY_DOWN" ; digital, move gun down
InputGunX = "MOUSE_XAXIS,JOY1_XAXIS" ; analog, full X axis
InputGunY = "MOUSE_YAXIS,JOY1_YAXIS" ; analog, full Y axis
InputTrigger = "KEY_A,JOY1_BUTTON1,MOUSE_LEFT_BUTTON"
InputOffscreen = "KEY_S,JOY1_BUTTON2,MOUSE_RIGHT_BUTTON" ; point off-screen
InputAutoTrigger = 0 ; automatic reload when off-screen
InputGunLeft2 = "NONE"
InputGunRight2 = "NONE"
InputGunUp2 = "NONE"
InputGunDown2 = "NONE"
InputGunX2 = "JOY2_XAXIS"
InputGunY2 = "JOY2_YAXIS"
InputTrigger2 = "JOY2_BUTTON1"
InputOffscreen2 = "JOY2_BUTTON2"
InputAutoTrigger2 = 0
; Analog guns (Ocean Hunter, LA Machineguns)
InputAnalogGunLeft = "KEY_LEFT" ; digital, move gun left
InputAnalogGunRight = "KEY_RIGHT" ; digital, move gun right
InputAnalogGunUp = "KEY_UP" ; digital, move gun up
InputAnalogGunDown = "KEY_DOWN" ; digital, move gun down
InputAnalogGunX = "MOUSE_XAXIS,JOY1_XAXIS" ; analog, full X axis
InputAnalogGunY = "MOUSE_YAXIS,JOY1_YAXIS" ; analog, full Y axis
InputAnalogTriggerLeft = "KEY_A,JOY1_BUTTON1,MOUSE_LEFT_BUTTON"
InputAnalogTriggerRight = "KEY_S,JOY1_BUTTON2,MOUSE_RIGHT_BUTTON"
InputAnalogGunLeft2 = "NONE"
InputAnalogGunRight2 = "NONE"
InputAnalogGunUp2 = "NONE"
InputAnalogGunDown2 = "NONE"
InputAnalogGunX2 = "NONE"
InputAnalogGunY2 = "NONE"
InputAnalogTriggerLeft2 = "NONE"
InputAnalogTriggerRight2 = "NONE"
; Ski Champ controls
InputSkiLeft = "KEY_LEFT"
InputSkiRight = "KEY_RIGHT"
InputSkiUp = "KEY_UP"
InputSkiDown = "KEY_DOWN"
InputSkiX = "JOY1_XAXIS"
InputSkiY = "JOY1_YAXIS"
InputSkiPollLeft = "KEY_A,JOY1_BUTTON1"
InputSkiPollRight = "KEY_S,JOY1_BUTTON2"
InputSkiSelect1 = "KEY_Q,JOY1_BUTTON3"
InputSkiSelect2 = "KEY_W,JOY1_BUTTON4"
InputSkiSelect3 = "KEY_E,JOY1_BUTTON5"
; Magical Truck Adventure controls
InputMagicalLeverUp1 = "KEY_UP"
InputMagicalLeverDown1 = "KEY_DOWN"
InputMagicalLeverUp2 = "NONE"
InputMagicalLeverDown2 = "NONE"
InputMagicalLever1 = "JOY1_YAXIS"
InputMagicalLever2 = "JOY2_YAXIS"
InputMagicalPedal1 = "KEY_A,JOY1_BUTTON1"
InputMagicalPedal2 = "KEY_S,JOY2_BUTTON1"
; Sega Bass Fishing / Get Bass controls
InputFishingRodLeft = "KEY_LEFT"
InputFishingRodRight = "KEY_RIGHT"
InputFishingRodUp = "KEY_UP"
InputFishingRodDown = "KEY_DOWN"
InputFishingStickLeft = "KEY_A"
InputFishingStickRight = "KEY_D"
InputFishingStickUp = "KEY_W"
InputFishingStickDown = "KEY_S"
InputFishingRodX = "JOY1_XAXIS"
InputFishingRodY = "JOY1_YAXIS"
InputFishingStickX = "JOY1_RXAXIS"
InputFishingStickY = "JOY1_RYAXIS"
InputFishingReel = "KEY_SPACE,JOY1_ZAXIS_POS"
InputFishingCast = "KEY_Z,JOY1_BUTTON1"
InputFishingSelect = "KEY_X,JOY1_BUTTON2"
+699
View File
@@ -0,0 +1,699 @@
#### ### ###
## ## ## ##
### ## ## ## ### #### ## ### ## ## #### ## #### ##
### ## ## ## ## ## ## ### ## ####### ## ## ##### ## ## ##
### ## ## ## ## ###### ## ## ####### ## ## ## ## ###### ##
## ## ## ## ##### ## ## ## # ## ## ## ## ## ## ##
#### ### ## ## #### #### ## ## #### ### ## #### ####
####
A Sega Model 3 Arcade Emulator.
Copyright 2011 Bart Trzynadlowski, Nik Henson
LICENSE AGREEMENT FOR SUPERMODEL VERSION 0.2A
Supermodel is distributed as free software under the terms of the GNU General
Public License, included below. See README.txt for additional documentation.
The source code may be obtained from the official Supermodel web site:
http://www.Supermodel3.com
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+1947
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
Source code for Third-party libraries needs to be placed in here.
+662
View File
@@ -0,0 +1,662 @@
DEBUG=0
PSS_STYLE=1
EXTERNAL_ZLIB=0
HAVE_GRIFFIN=1
STATIC_LINKING=0
ENDIANNESS_DEFINES=
SPACE :=
SPACE := $(SPACE) $(SPACE)
BACKSLASH :=
BACKSLASH := \$(BACKSLASH)
filter_out1 = $(filter-out $(firstword $1),$1)
filter_out2 = $(call filter_out1,$(call filter_out1,$1))
unixpath = $(subst \,/,$1)
unixcygpath = /$(subst :,,$(call unixpath,$1))
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
platform = osx
arch = intel
ifeq ($(shell uname -p),powerpc)
arch = ppc
endif
else ifneq ($(findstring win,$(shell uname -a)),)
platform = win
endif
endif
# system platform
system_platform = unix
ifeq ($(shell uname -a),)
EXE_EXT = .exe
system_platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
system_platform = osx
arch = intel
ifeq ($(shell uname -p),powerpc)
arch = ppc
endif
else ifneq ($(findstring MINGW,$(shell uname -a)),)
system_platform = win
endif
# Replace 'sample' with the name of the core
TARGET_NAME := sample
GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)"
ifneq ($(GIT_VERSION)," unknown")
CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
CXXFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
endif
ifneq (,$(findstring msvc,$(platform)))
LIBM :=
else
LIBM := -lm
endif
LIBS :=
CORE_DIR := .
# Unix
ifeq ($(platform), unix)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
ifneq ($(findstring SunOS,$(shell uname -a)),)
CC = gcc
SHARED := -shared -z defs
else
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined
endif
else ifeq ($(platform), linux-portable)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC -nostdlib
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T
LIBM :=
# OS X
else ifeq ($(platform), osx)
TARGET := $(TARGET_NAME)_libretro.dylib
fpic := -fPIC
SHARED := -dynamiclib
ifeq ($(arch),ppc)
ENDIANNESS_DEFINES += -DMSB_FIRST
endif
OSXVER = `sw_vers -productVersion | cut -d. -f 2`
OSX_LT_MAVERICKS = `(( $(OSXVER) <= 9)) && echo "YES"`
fpic += -mmacosx-version-min=10.1
ifndef ($(NOUNIVERSAL))
CFLAGS += $(ARCHFLAGS)
CXXFLAGS += $(ARCHFLAGS)
LDFLAGS += $(ARCHFLAGS)
endif
# iOS
else ifneq (,$(findstring ios,$(platform)))
TARGET := $(TARGET_NAME)_libretro_ios.dylib
fpic := -fPIC
SHARED := -dynamiclib
ifeq ($(IOSSDK),)
IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path)
endif
ifeq ($(platform),ios-arm64)
CC = cc -arch arm64 -isysroot $(IOSSDK)
else
CC = cc -arch armv7 -isysroot $(IOSSDK)
endif
CFLAGS += -DIOS
CXXFLAGS += -DIOS
ifeq ($(platform),$(filter $(platform),ios9 ios-arm64))
CC += -miphoneos-version-min=8.0
CFLAGS += -miphoneos-version-min=8.0
CXXFLAGS += -miphoneos-version-min=8.0
else
CC += -miphoneos-version-min=5.0
CFLAGS += -miphoneos-version-min=5.0
CXXFLAGS += -miphoneos-version-min=5.0
endif
# Theos iOS
else ifeq ($(platform), theos_ios)
DEPLOYMENT_IOSVERSION = 5.0
TARGET = iphone:latest:$(DEPLOYMENT_IOSVERSION)
ARCHS = armv7 armv7s
TARGET_IPHONEOS_DEPLOYMENT_VERSION=$(DEPLOYMENT_IOSVERSION)
THEOS_BUILD_DIR := objs
include $(THEOS)/makefiles/common.mk
LIBRARY_NAME = $(TARGET_NAME)_libretro_ios
# QNX
else ifeq ($(platform), qnx)
TARGET := $(TARGET_NAME)_libretro_qnx.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined
CC = qcc -Vgcc_ntoarmv7le
AR = qcc -Vgcc_ntoarmv7le
PLATFORM_DEFINES := -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
# PS3
else ifeq ($(platform), ps3)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe
PLATFORM_DEFINES := -D__CELLOS_LV2
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# sncps3
else ifeq ($(platform), sncps3)
TARGET := $(TARGET_NAME)_libretro_ps3.a
CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe
PLATFORM_DEFINES := -D__CELLOS_LV2
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# PSP
else ifeq ($(platform), psp1)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = psp-gcc$(EXE_EXT)
AR = psp-ar$(EXE_EXT)
PLATFORM_DEFINES := -DPSP -G0
PLATFORM_DEFINES += -march=allegrex -mfp32 -mgp32 -mlong32 -mabi=eabi
PLATFORM_DEFINES += -fomit-frame-pointer -fstrict-aliasing
PLATFORM_DEFINES += -falign-functions=32 -falign-loops -falign-labels -falign-jumps
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Vita
else ifeq ($(platform), vita)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = arm-vita-eabi-gcc$(EXE_EXT)
AR = arm-vita-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DVITA
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# DOS
else ifeq ($(platform), dos)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = i686-pc-msdosdjgpp-gcc
AR = i686-pc-msdosdjgpp-ar
CFLAGS += -march=i386
CXXFLAGS += -march=i386
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# CTR(3DS)
else ifeq ($(platform), ctr)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(DEVKITARM)/bin/arm-none-eabi-gcc$(EXE_EXT)
AR = $(DEVKITARM)/bin/arm-none-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DARM11 -D_3DS
CFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard
CFLAGS += -Wall -mword-relocations
CFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math
CXXFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard
CXXFLAGS += -Wall -mword-relocations
CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Raspberry Pi 1
else ifeq ($(platform), rpi1)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined
CFLAGS += -DARM11
CFLAGS += -marm -march=armv6j -mfpu=vfp -mfloat-abi=hard -funsafe-math-optimizations
CFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math
CXXFLAGS += -DARM11
CXXFLAGS += -marm -march=armv6j -mfpu=vfp -mfloat-abi=hard -funsafe-math-optimizations
CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math
# Raspberry Pi 2
else ifeq ($(platform), rpi2)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined
CFLAGS += -DARM
CFLAGS += -marm -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -funsafe-math-optimizations
CFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math
CXXFLAGS += -DARM
CXXFLAGS += -marm -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -funsafe-math-optimizations
CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math
# Lightweight PS3 Homebrew SDK
else ifeq ($(platform), psl1ght)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT)
AR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT)
PLATFORM_DEFINES := -D__CELLOS_LV2
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Xbox 360
else ifeq ($(platform), xenon)
TARGET := $(TARGET_NAME)_libretro_xenon360.a
CC = xenon-gcc$(EXE_EXT)
AR = xenon-ar$(EXE_EXT)
PLATFORM_DEFINES := -D__LIBXENON__
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Nintendo Game Cube
else ifeq ($(platform), ngc)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float
PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Nintendo Wii
else ifeq ($(platform), wii)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float
PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Nintendo WiiU
else ifeq ($(platform), wiiu)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DGEKKO -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float
PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# Nintendo Switch (libtransistor)
else ifeq ($(platform), switch)
EXT=a
TARGET := $(TARGET_NAME)_libretro_$(platform).$(EXT)
include $(LIBTRANSISTOR_HOME)/libtransistor.mk
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# ARM
else ifneq (,$(findstring armv,$(platform)))
TARGET := $(TARGET_NAME)_libretro.so
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined
fpic := -fPIC
ifneq (,$(findstring cortexa5,$(platform)))
PLATFORM_DEFINES += -marm -mcpu=cortex-a5
else ifneq (,$(findstring cortexa8,$(platform)))
PLATFORM_DEFINES += -marm -mcpu=cortex-a8
else ifneq (,$(findstring cortexa9,$(platform)))
PLATFORM_DEFINES += -marm -mcpu=cortex-a9
else ifneq (,$(findstring cortexa15a7,$(platform)))
PLATFORM_DEFINES += -marm -mcpu=cortex-a15.cortex-a7
else
PLATFORM_DEFINES += -marm
endif
ifneq (,$(findstring softfloat,$(platform)))
PLATFORM_DEFINES += -mfloat-abi=softfp
else ifneq (,$(findstring hardfloat,$(platform)))
PLATFORM_DEFINES += -mfloat-abi=hard
endif
PLATFORM_DEFINES += -DARM
# emscripten
else ifeq ($(platform), emscripten)
TARGET := $(TARGET_NAME)_libretro_$(platform).bc
STATIC_LINKING=1
EXTERNAL_ZLIB=1
# GCW0
else ifeq ($(platform), gcw0)
TARGET := $(TARGET_NAME)_libretro.so
CC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
CXX = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++
AR = /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar
fpic := -fPIC
SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined
PLATFORM_DEFINES += -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
EXTERNAL_ZLIB = 1
# Windows MSVC 2017 all architectures
else ifneq (,$(findstring windows_msvc2017,$(platform)))
PlatformSuffix = $(subst windows_msvc2017_,,$(platform))
ifneq (,$(findstring desktop,$(PlatformSuffix)))
WinPartition = desktop
MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -FS
LDFLAGS += -MANIFEST -LTCG:incremental -NXCOMPAT -DYNAMICBASE -DEBUG -OPT:REF -INCREMENTAL:NO -SUBSYSTEM:WINDOWS -MANIFESTUAC:"level='asInvoker' uiAccess='false'" -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1
LIBS += kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib
else ifneq (,$(findstring uwp,$(PlatformSuffix)))
WinPartition = uwp
MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_APP -DWINDLL -D_UNICODE -DUNICODE -DWRL_NO_DEFAULT_LIB -FS
LDFLAGS += -APPCONTAINER -NXCOMPAT -DYNAMICBASE -MANIFEST:NO -LTCG -OPT:REF -SUBSYSTEM:CONSOLE -MANIFESTUAC:NO -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 -DEBUG:FULL -WINMD:NO
LIBS += WindowsApp.lib
endif
CFLAGS += $(MSVC2017CompileFlags)
CXXFLAGS += $(MSVC2017CompileFlags)
TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix))
CC = cl.exe
CXX = cl.exe
LD = link.exe
reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul)))
fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1))
ProgramFiles86w := $(shell cmd /c "echo %PROGRAMFILES(x86)%")
ProgramFiles86 := $(shell cygpath "$(ProgramFiles86w)")
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0)
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0)
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0)
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0)
WindowsSdkDir := $(WindowsSdkDir)
WindowsSDKVersion ?= $(firstword $(foreach folder,$(subst $(subst \,/,$(WindowsSdkDir)Include/),,$(wildcard $(call fix_path,$(WindowsSdkDir)Include\*))),$(if $(wildcard $(call fix_path,$(WindowsSdkDir)Include/$(folder)/um/Windows.h)),$(folder),)))$(BACKSLASH)
WindowsSDKVersion := $(WindowsSDKVersion)
VsInstallBuildTools = $(ProgramFiles86)/Microsoft Visual Studio/2017/BuildTools
VsInstallEnterprise = $(ProgramFiles86)/Microsoft Visual Studio/2017/Enterprise
VsInstallProfessional = $(ProgramFiles86)/Microsoft Visual Studio/2017/Professional
VsInstallCommunity = $(ProgramFiles86)/Microsoft Visual Studio/2017/Community
VsInstallRoot ?= $(shell if [ -d "$(VsInstallBuildTools)" ]; then echo "$(VsInstallBuildTools)"; fi)
ifeq ($(VsInstallRoot), )
VsInstallRoot = $(shell if [ -d "$(VsInstallEnterprise)" ]; then echo "$(VsInstallEnterprise)"; fi)
endif
ifeq ($(VsInstallRoot), )
VsInstallRoot = $(shell if [ -d "$(VsInstallProfessional)" ]; then echo "$(VsInstallProfessional)"; fi)
endif
ifeq ($(VsInstallRoot), )
VsInstallRoot = $(shell if [ -d "$(VsInstallCommunity)" ]; then echo "$(VsInstallCommunity)"; fi)
endif
VsInstallRoot := $(VsInstallRoot)
VcCompilerToolsVer := $(shell cat "$(VsInstallRoot)/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" | grep -o '[0-9\.]*')
VcCompilerToolsDir := $(VsInstallRoot)/VC/Tools/MSVC/$(VcCompilerToolsVer)
WindowsSDKSharedIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\shared")
WindowsSDKUCRTIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\ucrt")
WindowsSDKUMIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\um")
WindowsSDKUCRTLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\ucrt\$(TargetArchMoniker)")
WindowsSDKUMLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\um\$(TargetArchMoniker)")
# For some reason the HostX86 compiler doesn't like compiling for x64
# ("no such file" opening a shared library), and vice-versa.
# Work around it for now by using the strictly x86 compiler for x86, and x64 for x64.
# NOTE: What about ARM?
ifneq (,$(findstring x64,$(TargetArchMoniker)))
VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX64
else
VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX86
endif
PATH := $(shell IFS=$$'\n'; cygpath "$(VCCompilerToolsBinDir)/$(TargetArchMoniker)"):$(PATH)
PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VsInstallRoot)/Common7/IDE")
INCLUDE := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/include")
LIB := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/lib/$(TargetArchMoniker)")
export INCLUDE := $(INCLUDE);$(WindowsSDKSharedIncludeDir);$(WindowsSDKUCRTIncludeDir);$(WindowsSDKUMIncludeDir)
export LIB := $(LIB);$(WindowsSDKUCRTLibDir);$(WindowsSDKUMLibDir)
TARGET := $(TARGET_NAME)_libretro.dll
PSS_STYLE :=2
LDFLAGS += -DLL
# Windows MSVC 2010 x64
else ifeq ($(platform), windows_msvc2010_x64)
CC = cl.exe
CXX = cl.exe
PATH := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin/amd64"):$(PATH)
PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../IDE")
INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/include")
LIB := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/lib/amd64")
BIN := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin")
WindowsSdkDir := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib/x64
WindowsSdkDir ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib/x64
export INCLUDE := $(INCLUDE)
export LIB := $(LIB);$(WindowsSdkDir)
TARGET := $(TARGET_NAME)_libretro.dll
PSS_STYLE :=2
LDFLAGS += -DLL
# Windows MSVC 2010 x86
else ifeq ($(platform), windows_msvc2010_x86)
CC = cl.exe
CXX = cl.exe
PATH := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin"):$(PATH)
PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../IDE")
INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/include")
LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS100COMNTOOLS)../../VC/lib")
BIN := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin")
WindowsSdkDir := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib
WindowsSdkDir ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib
export INCLUDE := $(INCLUDE)
export LIB := $(LIB);$(WindowsSdkDir)
TARGET := $(TARGET_NAME)_libretro.dll
PSS_STYLE :=2
LDFLAGS += -DLL
# Windows MSVC 2003 Xbox 1
else ifeq ($(platform), xbox1_msvc2003)
TARGET := $(TARGET_NAME)_libretro_xdk1.lib
CC = CL.exe
CXX = CL.exe
LD = lib.exe
export INCLUDE := $(XDK)\xbox\include
export LIB := $(XDK)\xbox\lib
PATH := $(call unixcygpath,$(XDK)/xbox/bin/vc71):$(PATH)
PSS_STYLE :=2
CFLAGS += -D_XBOX -D_XBOX1
CXXFLAGS += -D_XBOX -D_XBOX1
STATIC_LINKING=1
# Windows MSVC 2010 Xbox 360
else ifeq ($(platform), xbox360_msvc2010)
TARGET := $(TARGET_NAME)_libretro_xdk360.lib
CC = cl.exe
CXX = cl.exe
LD = lib.exe
export INCLUDE := $(XEDK)/include/xbox
export LIB := $(XEDK)/lib/xbox
PATH := $(call unixcygpath,$(XEDK)/bin/win32):$(PATH)
PSS_STYLE :=2
CFLAGS += -D_XBOX -D_XBOX360
CXXFLAGS += -D_XBOX -D_XBOX360
ENDIANNESS_DEFINES += -DMSB_FIRST
STATIC_LINKING=1
# Windows MSVC 2005 x86
else ifeq ($(platform), windows_msvc2005_x86)
CC = cl.exe
CXX = cl.exe
PATH := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/bin"):$(PATH)
PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../IDE")
INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/include")
LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS80COMNTOOLS)../../VC/lib")
BIN := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/bin")
WindowsSdkDir := $(INETSDK)
export INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005
export LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib
TARGET := $(TARGET_NAME)_libretro.dll
PSS_STYLE :=2
LDFLAGS += -DLL
CFLAGS += -D_CRT_SECURE_NO_DEPRECATE
# Windows MSVC 2003 x86
else ifeq ($(platform), windows_msvc2003_x86)
CC = cl.exe
CXX = cl.exe
PATH := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/bin"):$(PATH)
PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../IDE")
INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/include")
LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS71COMNTOOLS)../../Vc7/lib")
BIN := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/bin")
WindowsSdkDir := $(INETSDK)
export INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005
export LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib
TARGET := $(TARGET_NAME)_libretro.dll
PSS_STYLE :=2
LDFLAGS += -DLL
CFLAGS += -D_CRT_SECURE_NO_DEPRECATE
# Windows
else
TARGET := $(TARGET_NAME)_libretro.dll
CC = gcc
SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=$(CORE_DIR)/link.T
PSS_STYLE :=2
endif
ifeq ($(DEBUG), 1)
ifneq (,$(findstring msvc,$(platform)))
ifeq ($(STATIC_LINKING),1)
CFLAGS += -MTd
CXXFLAGS += -MTd
else
CFLAGS += -MDd
CXXFLAGS += -MDd
endif
CFLAGS += -Od -Zi -DDEBUG -D_DEBUG
CXXFLAGS += -Od -Zi -DDEBUG -D_DEBUG
else
CFLAGS += -O0 -g -DDEBUG
CXXFLAGS += -O0 -g -DDEBUG
endif
else
ifneq (,$(findstring msvc,$(platform)))
ifeq ($(STATIC_LINKING),1)
CFLAGS += -MT
CXXFLAGS += -MT
else
CFLAGS += -MD
CXXFLAGS += -MD
endif
CFLAGS += -O2 -DNDEBUG
CXXFLAGS += -O2 -DNDEBUG
else
CFLAGS += -O2 -DNDEBUG
CXXFLAGS += -O2 -DNDEBUG
endif
endif
ifeq ($(EXTERNAL_ZLIB), 1)
CFLAGS += -DHAVE_EXTERNAL_ZLIB
CXXFLAGS += -DHAVE_EXTERNAL_ZLIB
endif
include Makefile.common
OBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o)
DEFINES := $(COREDEFINES) $(PLATFORM_DEFINES)
ifeq ($(STATIC_LINKING),1)
DEFINES += -DSTATIC_LINKING
endif
ifeq ($(platform), sncps3)
WARNING_DEFINES =
else ifneq (,$(findstring msvc,$(platform)))
WARNING_DEFINES =
LIBM :=
else
WARNING_DEFINES = -Wno-write-strings
endif
CFLAGS += $(COREDEFINES) $(fpic) $(WARNING_DEFINES) $(DEFINES) $(ENDIANNESS_DEFINES)
CXXFLAGS += $(COREDEFINES) $(fpic) $(WARNING_DEFINES) $(DEFINES) $(ENDIANNESS_DEFINES)
LDFLAGS += $(LIBM)
ifeq ($(platform), psp1)
INCFLAGS += -I$(shell psp-config --pspsdk-path)/include
endif
ifneq (,$(findstring msvc,$(platform)))
OBJOUT = -Fo
LINKOUT = -out:
ifeq ($(STATIC_LINKING),1)
LD ?= lib.exe
STATIC_LINKING=0
else
LD = link.exe
endif
else
OBJOUT = -o
LINKOUT = -o
LD = $(CXX)
endif
ifeq ($(platform), theos_ios)
COMMON_FLAGS := -DIOS $(COMMON_DEFINES) $(INCFLAGS) -I$(THEOS_INCLUDE_PATH) -Wno-error
$(LIBRARY_NAME)_CFLAGS += $(COMMON_FLAGS) $(CFLAGS)
$(LIBRARY_NAME)_CXXFLAGS += $(COMMON_FLAGS) $(CXXFLAGS)
${LIBRARY_NAME}_FILES = $(SOURCES_C)
include $(THEOS_MAKE_PATH)/library.mk
else
all: $(TARGET)
$(TARGET): $(OBJECTS)
ifeq ($(STATIC_LINKING),1)
ifneq (,$(findstring msvc,$(platform)))
$(LD) $(LINKOUT)$@ $(OBJECTS)
else
$(AR) rcs $@ $(OBJECTS)
endif
else
$(LD) $(LINKOUT)$@ $(SHARED) $(OBJECTS) $(LDFLAGS) $(LIBS)
endif
%.o: %.cpp
$(CXX) -c $(OBJOUT)$@ $< $(CXXFLAGS) $(INCFLAGS)
%.o: %.c
$(CC) -c $(OBJOUT)$@ $< $(CFLAGS) $(INCFLAGS)
clean-objs:
rm -f $(OBJECTS)
clean:
rm -f $(OBJECTS)
rm -f $(TARGET)
.PHONY: clean clean-objs
endif
print-%:
@echo '$*=$($*)'
+73
View File
@@ -0,0 +1,73 @@
##
## Supermodel
## A Sega Model 3 Arcade Emulator.
## Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis
##
## This file is part of Supermodel.
##
## Supermodel is free software: you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation, either version 3 of the License, or (at your option)
## any later version.
##
## Supermodel is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
## more details.
##
## You should have received a copy of the GNU General Public License along
## with Supermodel. If not, see <http://www.gnu.org/licenses/>.
##
#
# Makefile.OSX
#
# Makefile for Mac OS X systems using gcc.
#
###############################################################################
# Build Options
###############################################################################
DELETE = rm -d -r -f
###############################################################################
# Platform Configuration
#
# Edit library and include paths as needed.
###############################################################################
#
# Toolchain
#
CC = gcc
CXX = g++
LD = gcc
#
# SDL
#
SDL_CFLAGS =
SDL_LIBS = -framework SDL -framework AGL -framework OpenGL -framework GLUT -framework Cocoa
#
# OSX-specific
#
PLATFORM_CFLAGS = $(SDL_CFLAGS) -DSUPERMODEL_OSX -DUSE_FILE32API -F/Library/Frameworks/
PLATFORM_LDFLAGS = $(SDL_LIBS) -lz -lm -lstdc++ -F/Library/Frameworks/
###############################################################################
# Core Makefile
###############################################################################
include Makefiles/Makefile.inc
clean:
$(SILENT)echo Cleaning up \"$(BIN_DIR)\" and \"$(OBJ_DIR)\"...
$(SILENT)$(DELETE) $(BIN_DIR)
$(SILENT)$(DELETE) $(OBJ_DIR)
+73
View File
@@ -0,0 +1,73 @@
##
## Supermodel
## A Sega Model 3 Arcade Emulator.
## Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis
##
## This file is part of Supermodel.
##
## Supermodel is free software: you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation, either version 3 of the License, or (at your option)
## any later version.
##
## Supermodel is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
## more details.
##
## You should have received a copy of the GNU General Public License along
## with Supermodel. If not, see <http://www.gnu.org/licenses/>.
##
#
# Makefile.UNIX
#
# Makefile for UNIX/Linux systems.
#
###############################################################################
# Build Options
###############################################################################
DELETE = rm -d -r -f
###############################################################################
# Platform Configuration
#
# Edit library and include paths as needed.
###############################################################################
#
# Toolchain
#
CC = gcc
CXX = g++
LD = gcc
#
# SDL
#
SDL_CFLAGS = `sdl-config --cflags`
SDL_LIBS = `sdl-config --libs`
#
# UNIX-specific
#
PLATFORM_CFLAGS = $(SDL_CFLAGS)
PLATFORM_LDFLAGS = $(SDL_LIBS) -lGL -lGLU -lz -lm -lstdc++
###############################################################################
# Core Makefile
###############################################################################
include Makefiles/Makefile.inc
clean:
$(SILENT)echo Cleaning up \"$(BIN_DIR)\" and \"$(OBJ_DIR)\"...
$(SILENT)$(DELETE) $(BIN_DIR)
$(SILENT)$(DELETE) $(OBJ_DIR)
+111
View File
@@ -0,0 +1,111 @@
##
## Supermodel
## A Sega Model 3 Arcade Emulator.
## Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis
##
## This file is part of Supermodel.
##
## Supermodel is free software: you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation, either version 3 of the License, or (at your option)
## any later version.
##
## Supermodel is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
## more details.
##
## You should have received a copy of the GNU General Public License along
## with Supermodel. If not, see <http://www.gnu.org/licenses/>.
##
#
# Makefile.Win32
#
# Makefile for Windows systems using gcc and the standard Command Prompt.
#
# Set variables in the "Configuration" section as required by your system
# configuration.
#
###############################################################################
# Configuration
#
# Edit as necessary. Some users may need to edit the "Platform Configuration"
# section as well, namely SDL_LIBS and PLATFORM_LIBS.
###############################################################################
#
# Bitness of build ('32' or '64')
#
BITS = 64
#
# Path to SDL
#
SDL_INCLUDE_DIR = c:/tdm-gcc-64/include/SDL
SDL_LIB_DIR = c:/tdm-gcc-64/lib
#
# Toolchain
#
CC = gcc
CXX = g++
LD = g++
###############################################################################
# Platform Configuration
###############################################################################
# Safety check: can be only '32' or '64' otherwise defaults to '64'
ifneq ($(filter $(strip $(BITS)),32 64),$(strip $(BITS)))
override BITS = 64
endif
#
# Use Bash or Windows Prompt shell commands?
#
DELETE = rm -d -r -f
ifeq ($(strip $(MSYSTEM)),)
DELETE = rmdir /s /q
endif
#
# SDL
#
#
# (At least for SDL) The linked libs order is fundamental for a successful compiling!
# https://stackoverflow.com/questions/24596596/sdl-doesnt-compile-in-native-enviroment
# http://www.cplusplus.com/forum/beginner/110753/
#
SDL_LIBS = -lmingw32 -static-libgcc -static-libstdc++ -lSDLmain -lSDL -lm -luser32 -lgdi32 -lwinmm -ldxguid
SDL_CFLAGS = -D_GNU_SOURCE=1 -Dmain=SDL_main
#
# MinGW/Windows-specific
#
PLATFORM_INCLUDE_DIR = $(SDL_INCLUDE_DIR)
PLATFORM_LIB_DIR = $(SDL_LIB_DIR)
PLATFORM_LIBS = -ldinput8 -lglu32 -lole32 -loleaut32 -lopengl32 -lwbemuuid -lws2_32 -lz
PLATFORM_CFLAGS = $(SDL_CFLAGS) -DSUPERMODEL_WIN32 $(addprefix -I,$(sort $(PLATFORM_INCLUDE_DIR)))
PLATFORM_LDFLAGS = -static -L$(sort $(PLATFORM_LIB_DIR)) $(SDL_LIBS) $(PLATFORM_LIBS)
###############################################################################
# Core Makefile
###############################################################################
PLATFORM_SRC_FILES = \
Src/OSD/Windows/DirectInputSystem.cpp \
Src/OSD/Windows/WinOutputs.cpp
include Makefiles/Makefile.inc
clean:
$(SILENT)echo Cleaning up $(BIN_DIR) and $(OBJ_DIR)...
$(SILENT)$(DELETE) $(BIN_DIR)
$(SILENT)$(DELETE) $(OBJ_DIR)
+351
View File
@@ -0,0 +1,351 @@
##
## Supermodel
## A Sega Model 3 Arcade Emulator.
## Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis
##
## This file is part of Supermodel.
##
## Supermodel is free software: you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation, either version 3 of the License, or (at your option)
## any later version.
##
## Supermodel is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
## more details.
##
## You should have received a copy of the GNU General Public License along
## with Supermodel. If not, see <http://www.gnu.org/licenses/>.
##
#
# Makefile.inc
#
# Core GNU Makefile, included by all (platform-specific) top-level Makefiles.
#
###############################################################################
# Build Options
###############################################################################
#
# Verbose progression
#
VERBOSE =
ifneq ($(filter $(strip $(VERBOSE)),0 1),$(strip $(VERBOSE)))
override VERBOSE =
endif
SILENT = @
ifeq ($(strip $(VERBOSE)),1)
SILENT =
endif
#
# Enable render state analyser for the legacy 3D engine (will slow down emulation!)
#
DEBUG =
ifneq ($(filter $(strip $(DEBUG)),0 1),$(strip $(DEBUG)))
override DEBUG =
endif
#
# Enable support for Model3 Net Board emulation
#
NET_BOARD =
ifneq ($(filter $(strip $(NET_BOARD)),0 1),$(strip $(NET_BOARD)))
override NET_BOARD =
endif
#
# Enable Bart's new (experimental) Real3D frame timing
#
NEW_FRAME_TIMING =
ifneq ($(filter $(strip $(NEW_FRAME_TIMING)),0 1),$(strip $(NEW_FRAME_TIMING)))
override NEW_FRAME_TIMING =
endif
#
# Include console-based debugger in emulator ('yes' or 'no')
#
ENABLE_DEBUGGER =
ifneq ($(filter $(strip $(ENABLE_DEBUGGER)),0 1),$(strip $(ENABLE_DEBUGGER)))
override ENABLE_DEBUGGER =
endif
###############################################################################
# Source Files
###############################################################################
SRC_FILES = \
Src/CPU/PowerPC/PPCDisasm.cpp \
Src/BlockFile.cpp \
Src/Pkgs/unzip.cpp \
Src/Pkgs/ioapi.cpp \
Src/Model3/93C46.cpp \
Src/Util/BitRegister.cpp \
Src/JTAG.cpp \
Src/Graphics/Legacy3D/Error.cpp \
Src/Pkgs/glew.cpp \
Src/Graphics/Shader.cpp \
Src/Model3/Real3D.cpp \
Src/Graphics/Legacy3D/Legacy3D.cpp \
Src/Graphics/Legacy3D/Models.cpp \
Src/Graphics/Legacy3D/TextureRefs.cpp \
Src/Graphics/New3D/New3D.cpp \
Src/Graphics/New3D/Mat4.cpp \
Src/Graphics/New3D/Model.cpp \
Src/Graphics/New3D/PolyHeader.cpp \
Src/Graphics/New3D/Texture.cpp \
Src/Graphics/New3D/TextureSheet.cpp \
Src/Graphics/New3D/VBO.cpp \
Src/Graphics/New3D/Vec.cpp \
Src/Graphics/New3D/R3DShader.cpp \
Src/Graphics/New3D/R3DFloat.cpp \
Src/Graphics/New3D/R3DScrollFog.cpp \
Src/Graphics/Render2D.cpp \
Src/Model3/TileGen.cpp \
Src/Model3/Model3.cpp \
Src/CPU/PowerPC/ppc.cpp \
Src/Model3/SoundBoard.cpp \
Src/Sound/SCSP.cpp \
Src/Sound/SCSPDSP.cpp \
Src/CPU/68K/68K.cpp \
$(OBJ_DIR)/m68kcpu.c \
$(OBJ_DIR)/m68kopnz.c \
$(OBJ_DIR)/m68kopdm.c \
$(OBJ_DIR)/m68kopac.c \
$(OBJ_DIR)/m68kops.c \
Src/Model3/DSB.cpp \
Src/CPU/Z80/Z80.cpp \
Src/Model3/IRQ.cpp \
Src/Model3/53C810.cpp \
Src/Model3/PCI.cpp \
Src/Model3/RTC72421.cpp \
Src/Model3/DriveBoard.cpp \
Src/Model3/MPC10x.cpp \
Src/Inputs/Input.cpp \
Src/Inputs/Inputs.cpp \
Src/Inputs/InputSource.cpp \
Src/Inputs/InputSystem.cpp \
Src/Inputs/InputTypes.cpp \
Src/Inputs/MultiInputSource.cpp \
Src/OSD/SDL/SDLInputSystem.cpp \
Src/OSD/Outputs.cpp \
Src/Sound/MPEG/amp_audio.cpp \
Src/Sound/MPEG/dump.cpp \
Src/Sound/MPEG/getbits.cpp \
Src/Sound/MPEG/getdata.cpp \
Src/Sound/MPEG/huffman.cpp \
Src/Sound/MPEG/layer2.cpp \
Src/Sound/MPEG/layer3.cpp \
Src/Sound/MPEG/misc2.cpp \
Src/Sound/MPEG/position.cpp \
Src/Sound/MPEG/transform.cpp \
Src/Sound/MPEG/util.cpp \
Src/Model3/Crypto.cpp \
Src/OSD/Logger.cpp \
Src/Util/Format.cpp \
Src/Util/NewConfig.cpp \
Src/Util/ByteSwap.cpp \
Src/Util/ConfigBuilders.cpp \
Src/GameLoader.cpp \
Src/Pkgs/tinyxml2.cpp \
Src/ROMSet.cpp \
$(PLATFORM_SRC_FILES)
ifeq ($(strip $(NET_BOARD)),1)
SRC_FILES += \
Src/Network/UDPReceive.cpp \
Src/Network/UDPSend.cpp \
Src/Network/WinSockWrap.cpp \
Src/Network/NetBoard.cpp
endif
ifeq ($(strip $(ENABLE_DEBUGGER)),1)
SRC_FILES += \
Src/Debugger/Debugger.cpp \
Src/Debugger/ConsoleDebugger.cpp \
Src/Debugger/SupermodelDebugger.cpp \
Src/Debugger/CPUDebug.cpp \
Src/Debugger/AddressTable.cpp \
Src/Debugger/Breakpoint.cpp \
Src/Debugger/CodeAnalyser.cpp \
Src/Debugger/Exception.cpp \
Src/Debugger/Interrupt.cpp \
Src/Debugger/DebuggerIO.cpp \
Src/Debugger/Label.cpp \
Src/Debugger/Register.cpp \
Src/Debugger/Watch.cpp \
Src/Debugger/CPU/PPCDebug.cpp \
Src/Debugger/CPU/68KDebug.cpp \
Src/Debugger/CPU/Musashi68KDebug.cpp \
Src/Debugger/CPU/Z80Debug.cpp
endif
#
# Sorted-path compile order
#
OBJ_FILES = $(foreach file,$(SRC_FILES),$(OBJ_DIR)/$(basename $(notdir $(file))).o)
#
# Deduce include directories from the source file list. The sort function
# removes duplicates and is used to construct a set.
#
INCLUDE_DIRS = $(sort $(foreach file,$(SRC_FILES),$(dir $(file))))
###############################################################################
# Output Locations
###############################################################################
OBJ_DIR = obj$(strip $(BITS))
BIN_DIR = bin$(strip $(BITS))
OUTFILE = supermodel
###############################################################################
# Compiler Flags
###############################################################################
ARCH = -march=native -mfpmath=sse
BUILD_CFLAGS =
OPT = -O3
WARN = -Wall
CSTD = -std=iso9899:2011
CXXSTD = -std=c++11
COMMON_CFLAGS = -c $(ARCH) $(OPT) $(WARN) $(addprefix -I,$(sort $(INCLUDE_DIRS))) -DGLEW_STATIC
CFLAGS = $(COMMON_CFLAGS) $(CSTD)
CXXFLAGS = $(PLATFORM_CFLAGS) $(COMMON_CFLAGS) $(BUILD_CFLAGS) $(CXXSTD)
LDFLAGS = -o $(BIN_DIR)/$(OUTFILE) $(PLATFORM_LDFLAGS) -s
# If render state analyser is enabled, need to define DEBUG
ifeq ($(strip $(DEBUG)),1)
BUILD_CFLAGS += -DDEBUG
endif
# If Net Board support is enabled, need to define NET_BOARD
ifeq ($(strip $(NET_BOARD)),1)
BUILD_CFLAGS += -DNET_BOARD
endif
# If Bart's new frame timing is enabled, need to define NEW_FRAME_TIMING
ifeq ($(strip $(NEW_FRAME_TIMING)),1)
BUILD_CFLAGS += -DNEW_FRAME_TIMING
endif
# If built-in debugger enabled, need to define SUPERMODEL_DEBUGGER
ifeq ($(strip $(ENABLE_DEBUGGER)),1)
BUILD_CFLAGS += -DSUPERMODEL_DEBUGGER
endif
###############################################################################
# Targets
###############################################################################
#
# Default target: build Supermodel binary. Must be specified *before* the auto-
# generated dependencies because otherwise, make gets confused for some reason
# and thinks the default target is just one of the object files.
#
all: $(BIN_DIR)/$(OUTFILE)
$(BIN_DIR)/$(OUTFILE): $(BIN_DIR) $(OBJ_DIR) $(OBJ_FILES)
$(info --------------------------------------------------------------------------------)
$(info Linking Supermodel : $(BIN_DIR)/$(OUTFILE))
$(SILENT)$(LD) $(OBJ_FILES) $(LDFLAGS)
$(info --------------------------------------------------------------------------------)
$(BIN_DIR):
$(info Creating directory : $(BIN_DIR))
$(SILENT)mkdir $(BIN_DIR)
$(OBJ_DIR):
$(info Creating directory : $(OBJ_DIR))
$(SILENT)mkdir $(OBJ_DIR)
###############################################################################
# Rules
###############################################################################
#
# Create list of auto-generated dependency files (which contain rules that make
# understands) and include them all.
#
AUTODEPS := $(patsubst %.o,%.d,$(OBJ_FILES))
-include $(AUTODEPS)
#
# VPATH is the search path for files. This trick allows a single %.cpp rule to
# be matched against files in sub-directories of Src/.
#
VPATH = $(INCLUDE_DIRS)
#
# Compilation rules that both auto-generate the dependency files and compile
# the source code. This technique is described in the reply by user "rr-" at:
# https://stackoverflow.com/questions/8025766/makefile-auto-dependency-generation
#
$(OBJ_DIR)/%.o: %.cpp
$(info Generating dependencies: $< -> $(OBJ_DIR)/$(*F).d)
$(SILENT)$(CXX) -MM -MP -MT $(OBJ_DIR)/$(*F).o -MT $(OBJ_DIR)/$(*F).d $(CXXFLAGS) $< > $(OBJ_DIR)/$(*F).d
$(info Compiling : $< -> $@)
$(SILENT)$(CXX) $(CXXFLAGS) $< -o $@
$(OBJ_DIR)/%.o: %.c
$(info Generating dependencies: $< -> $(OBJ_DIR)/$(*F).d)
$(SILENT)$(CC) -MM -MP -MT $(OBJ_DIR)/$(*F).o -MT $(OBJ_DIR)/$(*F).d $(CFLAGS) $< > $(OBJ_DIR)/$(*F).d
$(info Compiling : $< -> $@)
$(SILENT)$(CC) $(CFLAGS) $< -o $@
#
# Musashi 68K emulator
#
# All generated source files are emitted to the object directory. For MSVC,
# INLINE must be redefined as "static __inline", which is the syntax in C mode.
#
# The .exe suffix is absolutely required for native Windows non-MSYS builds!
#
MUSASHI_OUTFILE = $(OBJ_DIR)/m68kmake.exe # do not remove the .exe suffix!
MUSASHI_CFLAGS = -ISrc/CPU/68K/Musashi -I$(OBJ_DIR) -DINLINE="static inline" -Wno-unused-variable
MUSASHI_LDFLAGS = -o $(MUSASHI_OUTFILE) $(OBJ_DIR)/m68kmake.o -s
$(MUSASHI_OUTFILE): Src/CPU/68K/Musashi/m68kmake.c Src/CPU/68K/Musashi/m68k_in.c
$(info --------------------------------------------------------------------------------)
$(info Compiling : $< -> $(OBJ_DIR)/m68kmake.o)
$(SILENT)$(CC) $< $(CFLAGS) -o $(OBJ_DIR)/m68kmake.o
$(info Linking : $(MUSASHI_OUTFILE))
$(SILENT)$(LD) $(MUSASHI_LDFLAGS)
$(OBJ_DIR)/m68kops.h $(OBJ_DIR)/m68kops.c $(OBJ_DIR)/m68kopac.c $(OBJ_DIR)/m68kopdm.c $(OBJ_DIR)/m68kopnz.c: $(MUSASHI_OUTFILE) Src/CPU/68K/Musashi/m68k_in.c Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h
$(info Generating 68K emulator: $@)
@$(MUSASHI_OUTFILE) $(OBJ_DIR) Src/CPU/68K/Musashi/m68k_in.c
$(OBJ_DIR)/m68kcpu.o: Src/CPU/68K/Musashi/m68kcpu.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h
$(info Compiling : $< -> $@)
@$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $(OBJ_DIR)/m68kcpu.o
$(OBJ_DIR)/m68kops.o: $(OBJ_DIR)/m68kops.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE)
$(info Compiling : $< -> $@)
@$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@
$(OBJ_DIR)/m68kopac.o: $(OBJ_DIR)/m68kopac.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE)
$(info Compiling : $< -> $@)
@$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@
$(OBJ_DIR)/m68kopdm.o: $(OBJ_DIR)/m68kopdm.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE)
$(info Compiling : $< -> $@)
@$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@
$(OBJ_DIR)/m68kopnz.o: $(OBJ_DIR)/m68kopnz.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE)
$(info Compiling : $< -> $@)
@$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@
+278
View File
@@ -0,0 +1,278 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* BlockFile.cpp
*
* Block format container file management. Implementation of the CBlockFile
* class.
*/
#include <cstdio>
#include <cstring>
#include <cstdint>
#include "Supermodel.h"
/******************************************************************************
Output Functions
******************************************************************************/
void CBlockFile::ReadString(std::string *str, uint32_t length)
{
if (NULL == fp)
return;
str->clear();
//TODO: use fstream to get rid of this ugly hack
bool keep_loading = true;
for (size_t i = 0; i < length; i++)
{
char c;
fread(&c, sizeof(char), 1, fp);
if (keep_loading)
{
if (!c)
keep_loading = false;
else
*str += c;
}
}
}
unsigned CBlockFile::ReadBytes(void *data, uint32_t numBytes)
{
if (NULL == fp)
return 0;
return fread(data, sizeof(uint8_t), numBytes, fp);
}
unsigned CBlockFile::ReadDWord(uint32_t *data)
{
if (NULL == fp)
return 0;
fread(data, sizeof(uint32_t), 1, fp);
return 4;
}
void CBlockFile::UpdateBlockSize(void)
{
long int curPos;
unsigned newBlockSize;
if (NULL == fp)
return;
curPos = ftell(fp); // save current file position
fseek(fp, blockStartPos, SEEK_SET);
newBlockSize = curPos - blockStartPos;
fwrite(&newBlockSize, sizeof(uint32_t), 1, fp);
fseek(fp, curPos, SEEK_SET); // go back
}
void CBlockFile::WriteByte(uint8_t data)
{
if (NULL == fp)
return;
fwrite(&data, sizeof(uint8_t), 1, fp);
UpdateBlockSize();
}
void CBlockFile::WriteDWord(uint32_t data)
{
if (NULL == fp)
return;
fwrite(&data, sizeof(uint32_t), 1, fp);
UpdateBlockSize();
}
void CBlockFile::WriteBytes(const void *data, uint32_t numBytes)
{
if (NULL == fp)
return;
fwrite(data, sizeof(uint8_t), numBytes, fp);
UpdateBlockSize();
}
void CBlockFile::WriteBlockHeader(const std::string &name, const std::string &comment)
{
if (NULL == fp)
return;
// Record current block starting position
blockStartPos = ftell(fp);
// Write the total block length field
WriteDWord(0); // will be automatically updated as we write the file
// Write name and comment lengths
WriteDWord(name.size() + 1);
WriteDWord(comment.size() + 1);
Write(name);
Write(comment);
// Record the start of the current data section
dataStartPos = ftell(fp);
}
/******************************************************************************
Block Format Container File Implementation
Files are just a consecutive array of blocks that must be searched.
Block Format
------------
blockLength (uint32_t) Total length of block in bytes.
nameLength (uint32_t) Length of name field including terminating 0 (up to
1025).
commentLength (uint32_t) Same as above, but for comment string.
name ... Name string (null-terminated, up to 1025 bytes).
comment ... Comment string (same as above).
data ... Raw data (blockLength - total header size).
******************************************************************************/
unsigned CBlockFile::Read(void *data, uint32_t numBytes)
{
if (mode == 'r')
return ReadBytes(data, numBytes);
return 0;
}
unsigned CBlockFile::Read(bool *value)
{
uint8_t byte;
unsigned numBytes = Read(&byte, sizeof(byte));
if (numBytes > 0)
*value = byte != 0;
return numBytes;
}
void CBlockFile::Write(const void *data, uint32_t numBytes)
{
if (mode == 'w')
WriteBytes(data, numBytes);
}
void CBlockFile::Write(bool value)
{
uint8_t byte = value ? 1 : 0;
Write(&byte, sizeof(byte));
}
void CBlockFile::Write(const std::string &str)
{
if (mode == 'w')
WriteBytes(str.c_str(), str.length() + 1);
}
void CBlockFile::NewBlock(const std::string &name, const std::string &comment)
{
if (mode == 'w')
WriteBlockHeader(name, comment);
}
bool CBlockFile::FindBlock(const std::string &name)
{
if (mode != 'r')
return FAIL;
fseek(fp, 0, SEEK_SET);
long int curPos = 0;
while (curPos < fileSize)
{
blockStartPos = curPos;
// Read header
uint32_t block_length;
uint32_t name_length;
uint32_t comment_length;
curPos += ReadDWord(&block_length);
curPos += ReadDWord(&name_length);
curPos += ReadDWord(&comment_length);
std::string block_name;
ReadString(&block_name, name_length);
// Is this the block we want?
if (block_name == name)
{
fseek(fp, blockStartPos + 12 + name_length + comment_length, SEEK_SET); // move to beginning of data
dataStartPos = ftell(fp);
return OKAY;
}
// Move to next block
fseek(fp, blockStartPos + block_length, SEEK_SET);
curPos = blockStartPos + block_length;
if (block_length == 0) // this would never advance
break;
}
return FAIL;
}
bool CBlockFile::Create(const std::string &file, const std::string &headerName, const std::string &comment)
{
fp = fopen(file.c_str(), "wb");
if (NULL == fp)
return FAIL;
mode = 'w';
WriteBlockHeader(headerName, comment);
return OKAY;
}
bool CBlockFile::Load(const std::string &file)
{
fp = fopen(file.c_str(), "rb");
if (NULL == fp)
return FAIL;
mode = 'r';
// TODO: is this a valid block file?
// Get the file size
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
return OKAY;
}
void CBlockFile::Close(void)
{
if (fp != NULL)
fclose(fp);
fp = NULL;
mode = 0;
}
CBlockFile::CBlockFile(void)
{
fp = NULL;
mode = 0; // neither reading nor writing (do nothing)
}
CBlockFile::~CBlockFile(void)
{
if (fp != NULL) // in case user forgot
fclose(fp);
fp = NULL;
mode = 0;
}
+206
View File
@@ -0,0 +1,206 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* BlockFile.h
*
* Header file for block format container file management.
*/
#ifndef INCLUDED_BLOCKFILE_H
#define INCLUDED_BLOCKFILE_H
#include <cstdint>
#include <string>
/*
* CBlockFile:
*
* Block format container file. The file format is a series of consecutive,
* variable-length blocks referenced by unique name strings.
*
* All strings (comments and names) will be truncated to 1024 bytes, not
* including the null terminator.
*
* Members do not generate any output messages.
*/
class CBlockFile
{
public:
/*
* Read(data, numBytes):
*
* Reads data from the current file position.
*
* Parameters:
* data Buffer to read to.
* numBytes Number of bytes to read.
*
* Returns:
* Number of bytes read. If not the same as numBytes, an error
* occurred.
*/
unsigned Read(void *data, uint32_t numBytes);
/*
* Read(value):
*
* Reads a bool value from the current file position.
*
* Parameters:
* value Bool to read to.
*
* Returns:
* Number of bytes read. If not 1, an error occurred.
*/
unsigned Read(bool *value);
/*
* FindBlock(name):
*
* Scans the file for a block with the given name string. When it is found,
* the file pointer is set to the beginning of the data region.
*
* Parameters:
* name Name of block to locate.
*
* Returns:
* OKAY if found, FAIL if unable to locate.
*/
bool FindBlock(const std::string &name);
/*
* Write(value):
*
* Outputs a bool value at the current file pointer position as a byte with
* value either 0 or 1. Updates the block header appropriately.
*
* Parameters:
* value A boolean value to write.
*/
void Write(bool value);
/*
* Write(data, numBytes):
*
* Outputs data at the current file pointer position. Updates the block
* header appropriately.
*
* Parameters:
* data Data to write.
* numBytes Number of bytes to write.
*/
void Write(const void *data, uint32_t numBytes);
/*
* Write(str):
*
* Outputs string (including null terminator) at the current file pointer
* position. Updates the block header appropriately.
*
* Parameters:
* str String to write.
*/
void Write(const std::string &str);
/*
* NewBlock(name, comment):
*
* Begins a new block. Writes the block header and sets the file pointer to
* the beginning of its data area.
*
* Parameters:
* name Block name. Must be unique and not NULL.
* comment Comment string to embed in the block header.
*/
void NewBlock(const std::string &title, const std::string &comment);
/*
* Create(file, headerName, comment):
*
* Opens a block file for writing and creates the header block. This
* function must be called before attempting to write data. Otherwise, all
* write commands will be silently ignored. Read commands will be ignored
* and will always return 0's.
*
* Parameters:
* file File path.
* headerName Block name for header. Must be unique and not NULL.
* comment Comment string that will be embedded into file header.
*
* Returns:
* OKAY if successfully opened, otherwise FAIL.
*/
bool Create(const std::string &file, const std::string &headerName, const std::string &comment);
/*
* Load(file):
*
* Open a block file file for reading.
*
* Parameters:
* file File path.
*
* Returns:
* OKAY if successfully opened and confirmed to be a valid Supermodel
* block file, otherwise FAIL. If the file could not be opened, all
* subsequent operations will be silently ignored (reads will return
* 0's). Write commands will be ignored.
*/
bool Load(const std::string &file);
/*
* Close(void):
*
* Closes the file.
*/
void Close(void);
/*
* CBlockFile(void):
* ~CBlockFile(void):
*
* Constructor and destructor.
*/
CBlockFile(void);
~CBlockFile(void);
private:
// Helper functions
void ReadString(std::string *str, uint32_t length);
unsigned ReadBytes(void *data, uint32_t numBytes);
unsigned ReadDWord(uint32_t *data);
void UpdateBlockSize(void);
void WriteByte(uint8_t data);
void WriteDWord(uint32_t data);
void WriteBytes(const void *data, uint32_t numBytes);
void WriteBlockHeader(const std::string &name, const std::string &comment);
// File state data
FILE *fp;
int mode; // 'r' for read, 'w' for write
long int fileSize; // size of file in bytes
long int blockStartPos; // points to beginning of current block (or file) header
long int dataStartPos; // points to beginning of current block's data section
};
#endif // INCLUDED_BLOCKFILE_H
BIN
View File
Binary file not shown.
+449
View File
@@ -0,0 +1,449 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* 68K.cpp
*
* 68K CPU interface. This is presently just a wrapper for the Musashi 68K core
* and therefore, only a single CPU is supported. In the future, we may want to
* add in another 68K core (eg., Turbo68K, A68K, or a recompiler).
*
* To-Do List
* ----------
* - Registers may not completely describe the 68K state. Musashi also has
* additional CPU state information in the context that its MAME interface
* accesses (interrupts pending, halted status, etc.)
*/
#include "Supermodel.h"
#include "Musashi/m68k.h" // Musashi 68K core
/******************************************************************************
Internal Context
An active context must be mapped before calling M68K interface functions. Only
the bus and IRQ handlers are copied here; the CPU context is passed directly
to Musashi.
******************************************************************************/
// Bus
static IBus *s_Bus = NULL;
#ifdef SUPERMODEL_DEBUGGER
// Debugger
static Debugger::CMusashi68KDebug *s_Debug = NULL;
#endif
// IRQ callback
static int (*IRQAck)(int nIRQ) = NULL;
// Cycles remaining in timeslice
static int s_lastCycles;
/******************************************************************************
68K Interface
******************************************************************************/
// CPU state
UINT32 M68KGetARegister(int n)
{
m68k_register_t r;
switch (n)
{
case 0: r = M68K_REG_A0; break;
case 1: r = M68K_REG_A1; break;
case 2: r = M68K_REG_A2; break;
case 3: r = M68K_REG_A3; break;
case 4: r = M68K_REG_A4; break;
case 5: r = M68K_REG_A5; break;
case 6: r = M68K_REG_A6; break;
case 7: r = M68K_REG_A7; break;
default: r = M68K_REG_A7; break;
}
return m68k_get_reg(NULL, r);
}
UINT32 M68KGetDRegister(int n)
{
m68k_register_t r;
switch (n)
{
case 0: r = M68K_REG_D0; break;
case 1: r = M68K_REG_D1; break;
case 2: r = M68K_REG_D2; break;
case 3: r = M68K_REG_D3; break;
case 4: r = M68K_REG_D4; break;
case 5: r = M68K_REG_D5; break;
case 6: r = M68K_REG_D6; break;
case 7: r = M68K_REG_D7; break;
default: r = M68K_REG_D7; break;
}
return m68k_get_reg(NULL, r);
}
UINT32 M68KGetPC(void)
{
return m68k_get_reg(NULL, M68K_REG_PC);
}
void M68KSaveState(CBlockFile *StateFile, const char *name)
{
StateFile->NewBlock(name, __FILE__);
/*
* Rather than writing the context directly, the get/set register
* functions are used, ensuring that all context members are packed/
* unpacked correctly.
*
* Note: Some of these are undoubtedly 68010 or 68020 registers and not
* really necessary. But if the layout is changed now, the save state
* version has to be changed, so don't do it!
*/
UINT32 data[34];
m68ki_cpu_core Ctx;
m68k_get_context(&Ctx);
data[0] = Ctx.int_level;
data[1] = Ctx.int_cycles;
data[2] = Ctx.stopped;
data[3] = m68k_get_reg(NULL, M68K_REG_D0);
data[4] = m68k_get_reg(NULL, M68K_REG_D1);
data[5] = m68k_get_reg(NULL, M68K_REG_D2);
data[6] = m68k_get_reg(NULL, M68K_REG_D3);
data[7] = m68k_get_reg(NULL, M68K_REG_D4);
data[8] = m68k_get_reg(NULL, M68K_REG_D5);
data[9] = m68k_get_reg(NULL, M68K_REG_D6);
data[10] = m68k_get_reg(NULL, M68K_REG_D7);
data[11] = m68k_get_reg(NULL, M68K_REG_A0);
data[12] = m68k_get_reg(NULL, M68K_REG_A1);
data[13] = m68k_get_reg(NULL, M68K_REG_A2);
data[14] = m68k_get_reg(NULL, M68K_REG_A3);
data[15] = m68k_get_reg(NULL, M68K_REG_A4);
data[16] = m68k_get_reg(NULL, M68K_REG_A5);
data[17] = m68k_get_reg(NULL, M68K_REG_A6);
data[18] = m68k_get_reg(NULL, M68K_REG_A7);
data[19] = m68k_get_reg(NULL, M68K_REG_PC);
data[20] = m68k_get_reg(NULL, M68K_REG_SR);
data[21] = m68k_get_reg(NULL, M68K_REG_SP);
data[22] = m68k_get_reg(NULL, M68K_REG_USP);
data[23] = m68k_get_reg(NULL, M68K_REG_ISP);
data[24] = m68k_get_reg(NULL, M68K_REG_MSP);
data[25] = m68k_get_reg(NULL, M68K_REG_SFC);
data[26] = m68k_get_reg(NULL, M68K_REG_DFC);
data[27] = m68k_get_reg(NULL, M68K_REG_VBR);
data[28] = m68k_get_reg(NULL, M68K_REG_CACR);
data[29] = m68k_get_reg(NULL, M68K_REG_CAAR);
data[30] = m68k_get_reg(NULL, M68K_REG_PREF_ADDR);
data[31] = m68k_get_reg(NULL, M68K_REG_PREF_DATA);
data[32] = m68k_get_reg(NULL, M68K_REG_PPC);
data[33] = m68k_get_reg(NULL, M68K_REG_IR);
StateFile->Write(data, sizeof(data));
}
void M68KLoadState(CBlockFile *StateFile, const char *name)
{
if (OKAY != StateFile->FindBlock(name))
{
ErrorLog("Unable to load 68K state. Save state file is corrupt.");
return;
}
UINT32 data[34];
m68ki_cpu_core Ctx;
StateFile->Read(data, sizeof(data));
// These must be set first, to ensure another contexts' IRQs aren't active when PC is changed
m68k_get_context(&Ctx);
Ctx.int_level = data[0];
Ctx.int_cycles = data[1];
Ctx.stopped = data[2];
m68k_set_context(&Ctx); // write them back to context
m68k_set_reg(M68K_REG_D0, data[3]);
m68k_set_reg(M68K_REG_D1, data[4]);
m68k_set_reg(M68K_REG_D2, data[5]);
m68k_set_reg(M68K_REG_D3, data[6]);
m68k_set_reg(M68K_REG_D4, data[7]);
m68k_set_reg(M68K_REG_D5, data[8]);
m68k_set_reg(M68K_REG_D6, data[9]);
m68k_set_reg(M68K_REG_D7, data[10]);
m68k_set_reg(M68K_REG_A0, data[11]);
m68k_set_reg(M68K_REG_A1, data[12]);
m68k_set_reg(M68K_REG_A2, data[13]);
m68k_set_reg(M68K_REG_A3, data[14]);
m68k_set_reg(M68K_REG_A4, data[15]);
m68k_set_reg(M68K_REG_A5, data[16]);
m68k_set_reg(M68K_REG_A6, data[17]);
m68k_set_reg(M68K_REG_A7, data[18]);
m68k_set_reg(M68K_REG_PC, data[19]);
m68k_set_reg(M68K_REG_SR, data[20]);
m68k_set_reg(M68K_REG_SP, data[21]);
m68k_set_reg(M68K_REG_USP, data[22]);
m68k_set_reg(M68K_REG_ISP, data[23]);
m68k_set_reg(M68K_REG_MSP, data[24]);
m68k_set_reg(M68K_REG_SFC, data[25]);
m68k_set_reg(M68K_REG_DFC, data[26]);
m68k_set_reg(M68K_REG_VBR, data[27]);
m68k_set_reg(M68K_REG_CACR, data[28]);
m68k_set_reg(M68K_REG_CAAR, data[29]);
m68k_set_reg(M68K_REG_PREF_ADDR, data[30]);
m68k_set_reg(M68K_REG_PREF_DATA, data[31]);
m68k_set_reg(M68K_REG_PPC, data[32]);
m68k_set_reg(M68K_REG_IR, data[33]);
}
// Emulation functions
void M68KSetIRQ(int irqLevel)
{
m68k_set_irq(irqLevel);
}
int M68KRun(int numCycles)
{
#ifdef SUPERMODEL_DEBUGGER
if (s_Debug != NULL)
{
s_Debug->CPUActive();
s_lastCycles += numCycles;
}
#endif // SUPERMODEL_DEBUGGER
int doneCycles = m68k_execute(numCycles);
#ifdef SUPERMODEL_DEBUGGER
if (s_Debug != NULL)
{
s_Debug->CPUInactive();
s_lastCycles -= m68k_cycles_remaining();
}
#endif // SUPERMODEL_DEBUGGER
return doneCycles;
}
void M68KReset(void)
{
m68k_pulse_reset();
#ifdef SUPERMODEL_DEBUGGER
s_lastCycles = 0;
#endif
DebugLog("68K reset\n");
}
// Callback setup
void M68KSetIRQCallback(int (*F)(int nIRQ))
{
IRQAck = F;
}
void M68KAttachBus(IBus *BusPtr)
{
s_Bus = BusPtr;
DebugLog("Attached bus to 68K\n");
}
// Context switching
void M68KGetContext(M68KCtx *Dest)
{
Dest->IRQAck = IRQAck;
Dest->Bus = s_Bus;
#ifdef SUPERMODEL_DEBUGGER
Dest->Debug = s_Debug;
#endif // SUPERMODEL_DEBUGGER
m68k_get_context(&(Dest->musashiCtx));
}
void M68KSetContext(M68KCtx *Src)
{
IRQAck = Src->IRQAck;
s_Bus = Src->Bus;
#ifdef SUPERMODEL_DEBUGGER
s_Debug = Src->Debug;
#endif // SUPERMODEL_DEBUGGER
m68k_set_context(&(Src->musashiCtx));
}
// One-time initialization
bool M68KInit(void)
{
m68k_init();
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
m68k_set_int_ack_callback(M68KIRQCallback);
s_Bus = NULL;
#ifdef SUPERMODEL_DEBUGGER
s_Debug = NULL;
#endif // SUPERMODEL_DEBUGGER
DebugLog("Initialized 68K\n");
return OKAY;
}
#ifdef SUPERMODEL_DEBUGGER
UINT32 M68KGetRegister(M68KCtx *Src, unsigned reg)
{
switch (reg)
{
case DBG68K_REG_PC: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_PC);
case DBG68K_REG_SR: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_SR);
case DBG68K_REG_SP: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_SP);
case DBG68K_REG_D0: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D0);
case DBG68K_REG_D1: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D1);
case DBG68K_REG_D2: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D2);
case DBG68K_REG_D3: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D3);
case DBG68K_REG_D4: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D4);
case DBG68K_REG_D5: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D5);
case DBG68K_REG_D6: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D6);
case DBG68K_REG_D7: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D7);
case DBG68K_REG_A0: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A0);
case DBG68K_REG_A1: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A1);
case DBG68K_REG_A2: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A2);
case DBG68K_REG_A3: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A3);
case DBG68K_REG_A4: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A4);
case DBG68K_REG_A5: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A5);
case DBG68K_REG_A6: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A6);
case DBG68K_REG_A7: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A7);
default: return 0;
}
}
UINT32 M68KSetRegister(M68KCtx *Src, unsigned reg, UINT32 val)
{
switch (reg)
{
case DBG68K_REG_PC: m68k_set_reg(M68K_REG_PC, val); return true;
case DBG68K_REG_SR: m68k_set_reg(M68K_REG_SR, val); return true;
case DBG68K_REG_SP: m68k_set_reg(M68K_REG_SP, val); return true;
case DBG68K_REG_D0: m68k_set_reg(M68K_REG_D0, val); return true;
case DBG68K_REG_D1: m68k_set_reg(M68K_REG_D1, val); return true;
case DBG68K_REG_D2: m68k_set_reg(M68K_REG_D2, val); return true;
case DBG68K_REG_D3: m68k_set_reg(M68K_REG_D3, val); return true;
case DBG68K_REG_D4: m68k_set_reg(M68K_REG_D4, val); return true;
case DBG68K_REG_D5: m68k_set_reg(M68K_REG_D5, val); return true;
case DBG68K_REG_D6: m68k_set_reg(M68K_REG_D6, val); return true;
case DBG68K_REG_D7: m68k_set_reg(M68K_REG_D7, val); return true;
case DBG68K_REG_A0: m68k_set_reg(M68K_REG_A0, val); return true;
case DBG68K_REG_A1: m68k_set_reg(M68K_REG_A1, val); return true;
case DBG68K_REG_A2: m68k_set_reg(M68K_REG_A2, val); return true;
case DBG68K_REG_A3: m68k_set_reg(M68K_REG_A3, val); return true;
case DBG68K_REG_A4: m68k_set_reg(M68K_REG_A4, val); return true;
case DBG68K_REG_A5: m68k_set_reg(M68K_REG_A5, val); return true;
case DBG68K_REG_A6: m68k_set_reg(M68K_REG_A6, val); return true;
case DBG68K_REG_A7: m68k_set_reg(M68K_REG_A7, val); return true;
default: return false;
}
}
#endif // SUPERMODEL_DEBUGGER
/******************************************************************************
Musashi 68K Handlers
Musashi/m68kconf.h has been configured to call these directly.
******************************************************************************/
extern "C" {
#ifdef SUPERMODEL_DEBUGGER
void M68KDebugCallback()
{
if (s_Debug != NULL)
{
UINT32 pc = m68k_get_reg(NULL, M68K_REG_PC);
UINT32 opcode = s_Bus->Read16(pc);
s_Debug->CPUExecute(pc, opcode, s_lastCycles - m68k_cycles_remaining());
s_lastCycles = m68k_cycles_remaining();
}
}
#endif // SUPERMODEL_DEBUGGER
int M68KIRQCallback(int nIRQ)
{
#ifdef SUPERMODEL_DEBUGGER
if (s_Debug != NULL)
{
s_Debug->CPUException(25);
s_Debug->CPUInterrupt(nIRQ - 1);
}
#endif // SUPERMODEL_DEBUGGER
if (NULL == IRQAck) // no handler, use default behavior
{
m68k_set_irq(0); // clear line
return M68K_IRQ_AUTOVECTOR;
}
else
return IRQAck(nIRQ);
}
unsigned int FASTCALL M68KFetch8(unsigned int a)
{
return s_Bus->Read8(a);
}
unsigned int FASTCALL M68KFetch16(unsigned int a)
{
return s_Bus->Read16(a);
}
unsigned int FASTCALL M68KFetch32(unsigned int a)
{
return s_Bus->Read32(a);
}
unsigned int FASTCALL M68KRead8(unsigned int a)
{
return s_Bus->Read8(a);
}
unsigned int FASTCALL M68KRead16(unsigned int a)
{
return s_Bus->Read16(a);
}
unsigned int FASTCALL M68KRead32(unsigned int a)
{
return s_Bus->Read32(a);
}
void FASTCALL M68KWrite8(unsigned int a, unsigned int d)
{
s_Bus->Write8(a, d);
}
void FASTCALL M68KWrite16(unsigned int a, unsigned int d)
{
s_Bus->Write16(a, d);
}
void FASTCALL M68KWrite32(unsigned int a, unsigned int d)
{
s_Bus->Write32(a, d);
}
} // extern "C"
+392
View File
@@ -0,0 +1,392 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* 68K.h
*
* Header file for 68K CPU interface. Caution: 68K emulator is not thread-safe.
*
* TO-DO List:
* -----------
* - Optimize things, perhaps by using FASTCALL
*/
#ifndef INCLUDED_68K_H
#define INCLUDED_68K_H
#include <cstring>
#include "Types.h"
#include "Musashi/m68k.h"
#include "Musashi/m68kctx.h"
#include "CPU/Bus.h"
// This doesn't work for now (needs to be added to the prototypes in m68k.h for m68k_read_memory*)
//#ifndef FASTCALL
#undef FASTCALL
#define FASTCALL
//#endif
#ifdef SUPERMODEL_DEBUGGER
namespace Debugger {
class CMusashi68KDebug;
}
#endif // SUPERMODEL_DEBUGGER
/******************************************************************************
Definitions
******************************************************************************/
// IRQ callback special return values
#define M68K_IRQ_AUTOVECTOR M68K_INT_ACK_AUTOVECTOR // signals an autovectored interrupt
#define M68K_IRQ_SPURIOUS M68K_INT_ACK_SPURIOUS // signals a spurious interrupt
/******************************************************************************
CPU Context
******************************************************************************/
/*
* M68KCtx:
*
* Complete state of a single 68K. Do NOT manipulate these directly. Set the
* context and then use the M68K* functions below to attach a bus and IRQ
* callback to the active context.
*/
typedef struct SM68KCtx
{
public:
m68ki_cpu_core musashiCtx; // CPU context
IBus *Bus; // memory handlers
int (*IRQAck)(int); // IRQ acknowledge callback
#ifdef SUPERMODEL_DEBUGGER
Debugger::CMusashi68KDebug *Debug; // holds debugger (if attached)
#endif // SUPERMODEL_DEBUGGER
SM68KCtx(void)
{
Bus = NULL;
IRQAck = NULL;
memset(&musashiCtx, 0, sizeof(musashiCtx)); // very important! garbage in context at reset can cause very strange bugs
#ifdef SUPERMODEL_DEBUGGER
Debug = NULL;
#endif // SUPERMODEL_DEBUGGER
}
~SM68KCtx(void)
{
Bus = NULL;
IRQAck = NULL;
}
} M68KCtx;;
/******************************************************************************
68K Interface
Unless otherwise noted, all functions operate on the active context.
******************************************************************************/
/*
* M68KGetARegister(n):
*
* Parameters:
* n Register number (0-7).
*
* Returns:
* Register An.
*/
extern UINT32 M68KGetARegister(int n);
/*
* M68KSetARegister(n, v):
*
* Parameters:
* n Register number (0-7).
* v Value to set register An to.
*/
extern void M68KSetARegister(int n, UINT32 v);
/*
* M68KGetDRegister(n):
*
* Parameters:
* n Register number (0-7).
*
* Returns:
* Register Dn.
*/
extern UINT32 M68KGetDRegister(int n);
/*
* M68KGetDRegister(n, v):
*
* Parameters:
* n Register number (0-7).
* v Value to set register Dn to.
*/
extern void M68KSetDRegister(int n, UINT32 v);
/*
* M68KGetPC():
*
* Returns:
* Current program counter.
*/
extern UINT32 M68KGetPC(void);
/*
* M68KSaveState(StateFile, name):
*
* Saves the CPU state to the block file.
*
* Parameters:
* StateFile Block file.
* name Name of block to create (e.g. "Main 68K"), to facilitate
* multiple 68K states in the same file.
*/
extern void M68KSaveState(CBlockFile *StateFile, const char *name);
/*
* M68KLoadState(StateFile, name):
*
* Loads the CPU state.
*
* Parameters:
* StateFile Block file.
* name Name of block to load.
*/
extern void M68KLoadState(CBlockFile *StateFile, const char *name);
/*
* M68KSetIRQ(irqLevel):
*
* Sets the interrupt level (IPL2-IPL0 pins).
*
* Parameters:
* irqLevel The interrupt level (1-7) or 0 to clear the interrupt.
*/
extern void M68KSetIRQ(int irqLevel);
/*
* M68KRun(numCycles):
*
* Runs the 68K.
*
* Parameters:
* numCycles Number of cycles to run.
*
* Returns:
* Number of cycles executed.
*/
extern int M68KRun(int numCycles);
/*
* M68KReset():
*
* Resets the 68K.
*/
extern void M68KReset(void);
/*
* M68KSetIRQCallback(F):
*
* Installs an interrupt acknowledge callback for the currently active CPU. The
* default behavior is to always assume autovectored interrupts.
*
* Parameters:
* F Callback function.
*/
extern void M68KSetIRQCallback(int (*F)(int));
/*
* M68KAttachBus(IBus *BusPtr):
*
* Attaches a bus object to the 68K, which will be used to perform all address
* space accesses. The 8, 16, and 32-bit read and write handlers are used.
* This must be set up before any 68K-related emulation functions are invoked
* or the program will crash!
*
* Parameters:
* BusPtr Pointer to bus object to use for all address space accesses.
*/
extern void M68KAttachBus(IBus *BusPtr);
/*
* M68KInit():
*
* Initializes the 68K emulator. Must be called once per program session prior
* to any 68K emulation functions. A context must be mapped before calling
* this.
*
* Returns:
* Always returns OKAY.
*/
extern bool M68KInit(void);
/*
* M68KGetContext(M68KCtx *Dest):
*
* Copies the internal (active) 68K context back to the destination.
*
* Parameters:
* Dest Location to which to copy 68K context.
*/
extern void M68KGetContext(M68KCtx *Dest);
/*
* M68KSetContext(M68KCtx *Src):
*
* Sets the specified 68K context by copying it to the internal context.
*
* Parameters:
* Src Location from which to copy 68K context.
*/
extern void M68KSetContext(M68KCtx *Src);
#ifdef SUPERMODEL_DEBUGGER
#define DBG68K_REG_PC 0
#define DBG68K_REG_SR 1
#define DBG68K_REG_SP 2
#define DBG68K_REG_D0 3
#define DBG68K_REG_D1 4
#define DBG68K_REG_D2 5
#define DBG68K_REG_D3 6
#define DBG68K_REG_D4 7
#define DBG68K_REG_D5 8
#define DBG68K_REG_D6 9
#define DBG68K_REG_D7 10
#define DBG68K_REG_A0 11
#define DBG68K_REG_A1 12
#define DBG68K_REG_A2 13
#define DBG68K_REG_A3 14
#define DBG68K_REG_A4 15
#define DBG68K_REG_A5 16
#define DBG68K_REG_A6 17
#define DBG68K_REG_A7 18
/*
* M68KGetRegister(ctx, reg):
*
* Parameters:
* ctx Musashi 68K context.
* reg Register (use DBG68K_REG_* macros).
*
* Returns:
* Register value.
*/
UINT32 M68KGetRegister(SM68KCtx *ctx, unsigned reg);
/*
* M68KSetRegister(ctx, reg, value):
*
* Parameters:
* ctx Musashi 68K context.
* reg Register (use DBG68K_REG_* macros).
* value Vale to write.
*
* Returns:
* True if successful, false if register does not exist.
*/
UINT32 M68KSetRegister(SM68KCtx *ctx, unsigned reg, UINT32 val);
#endif // SUPERMODEL_DEBUGGER
/******************************************************************************
68K Handlers
Intended for use directly by the 68K core.
******************************************************************************/
extern "C" {
#ifdef SUPERMODEL_DEBUGGER
extern void M68KDebugCallback();
#endif // SUPERMODEL_DEBUGGER
/*
* M68KIRQCallback(nIRQ):
*
* Interrupt acknowledge callback. Called when an interrupt is being serviced.
* Actually implemented by calling a user-supplied callback.
*
* Parameters:
* nIRQ IRQ level (1-7).
*
* Returns:
* The interrupt vector to use, M68K_IRQ_AUTOVECTOR, or
* M68K_IRQ_SPURIOUS. If no callback has been installed, the default
* callback is used and always returns M68K_IRQ_AUTOVECTOR.
*/
extern int M68KIRQCallback(int nIRQ);
/*
* M68KFetch8(a):
* M68KFetch16(a):
* M68KFetch32(a):
*
* Read data from the program address space.
*
* Parameters:
* a Address to read from.
*
* Returns:
* The 8, 16, or 32-bit value read.
*/
unsigned int FASTCALL M68KFetch8(unsigned int a);
unsigned int FASTCALL M68KFetch16(unsigned int a);
unsigned int FASTCALL M68KFetch32(unsigned int a);
/*
* M68KRead8(a):
* M68KRead16(a):
* M68KRead32(a):
*
* Read data from the data address space.
*
* Parameters:
* a Address to read from.
*
* Returns:
* The 8, 16, or 32-bit value read.
*/
unsigned int FASTCALL M68KRead8(unsigned int a);
unsigned int FASTCALL M68KRead16(unsigned int a);
unsigned int FASTCALL M68KRead32(unsigned int a);
/*
* M68KWrite8(a, d):
* M68KWrite16(a, d):
* M68KWrite32(a, d):
*
* Writes to the data address space.
*
* Parameters:
* a Address to write to.
* d Data to write.
*/
void FASTCALL M68KWrite8(unsigned int a, unsigned int d);
void FASTCALL M68KWrite16(unsigned int a, unsigned int d);
void FASTCALL M68KWrite32(unsigned int a, unsigned int d);
} // extern "C"
#endif // INCLUDED_68K_H
BIN
View File
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
Binary file not shown.
+395
View File
@@ -0,0 +1,395 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* m68k.h
*
* Header file for the Musashi 68K emulator.
*
* Permission was obtained from Karl Stenerud to apply the GPL license to this
* code.
*/
#ifndef M68K__HEADER
#define M68K__HEADER
#ifdef __cplusplus
extern "C" {
#endif
/* ======================================================================== */
/* ========================= LICENSING & COPYRIGHT ======================== */
/* ======================================================================== */
/*
* MUSASHI
* Version 3.3
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
*
* This code may be freely used for non-commercial purposes as long as this
* copyright notice remains unaltered in the source code and any binary files
* containing this code in compiled form.
*
* All other lisencing terms must be negotiated with the author
* (Karl Stenerud).
*
* The latest version of this code can be obtained at:
* http://kstenerud.cjb.net
*/
/* ======================================================================== */
/* ============================ GENERAL DEFINES =========================== */
/* ======================================================================== */
/* There are 7 levels of interrupt to the 68K.
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
*/
#define M68K_IRQ_NONE 0
#define M68K_IRQ_1 1
#define M68K_IRQ_2 2
#define M68K_IRQ_3 3
#define M68K_IRQ_4 4
#define M68K_IRQ_5 5
#define M68K_IRQ_6 6
#define M68K_IRQ_7 7
/* Special interrupt acknowledge values.
* Use these as special returns from the interrupt acknowledge callback
* (specified later in this header).
*/
/* Causes an interrupt autovector (0x18 + interrupt level) to be taken.
* This happens in a real 68K if VPA or AVEC is asserted during an interrupt
* acknowledge cycle instead of DTACK.
*/
#define M68K_INT_ACK_AUTOVECTOR 0xffffffff
/* Causes the spurious interrupt vector (0x18) to be taken
* This happens in a real 68K if BERR is asserted during the interrupt
* acknowledge cycle (i.e. no devices responded to the acknowledge).
*/
#define M68K_INT_ACK_SPURIOUS 0xfffffffe
/* CPU types for use in m68k_set_cpu_type() */
enum
{
M68K_CPU_TYPE_INVALID,
M68K_CPU_TYPE_68000,
M68K_CPU_TYPE_68008,
M68K_CPU_TYPE_68010,
M68K_CPU_TYPE_68EC020,
M68K_CPU_TYPE_68020,
M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */
M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */
};
/* Registers used by m68k_get_reg() and m68k_set_reg() */
typedef enum
{
/* Real registers */
M68K_REG_D0, /* Data registers */
M68K_REG_D1,
M68K_REG_D2,
M68K_REG_D3,
M68K_REG_D4,
M68K_REG_D5,
M68K_REG_D6,
M68K_REG_D7,
M68K_REG_A0, /* Address registers */
M68K_REG_A1,
M68K_REG_A2,
M68K_REG_A3,
M68K_REG_A4,
M68K_REG_A5,
M68K_REG_A6,
M68K_REG_A7,
M68K_REG_PC, /* Program Counter */
M68K_REG_SR, /* Status Register */
M68K_REG_SP, /* The current Stack Pointer (located in A7) */
M68K_REG_USP, /* User Stack Pointer */
M68K_REG_ISP, /* Interrupt Stack Pointer */
M68K_REG_MSP, /* Master Stack Pointer */
M68K_REG_SFC, /* Source Function Code */
M68K_REG_DFC, /* Destination Function Code */
M68K_REG_VBR, /* Vector Base Register */
M68K_REG_CACR, /* Cache Control Register */
M68K_REG_CAAR, /* Cache Address Register */
/* Assumed registers */
/* These are cheat registers which emulate the 1-longword prefetch
* present in the 68000 and 68010.
*/
M68K_REG_PREF_ADDR, /* Last prefetch address */
M68K_REG_PREF_DATA, /* Last prefetch data */
/* Convenience registers */
M68K_REG_PPC, /* Previous value in the program counter */
M68K_REG_IR, /* Instruction register */
M68K_REG_CPU_TYPE /* Type of CPU being run */
} m68k_register_t;
/* ======================================================================== */
/* ====================== FUNCTIONS CALLED BY THE CPU ===================== */
/* ======================================================================== */
/* You will have to implement these functions */
/* read/write functions called by the CPU to access memory.
* while values used are 32 bits, only the appropriate number
* of bits are relevant (i.e. in write_memory_8, only the lower 8 bits
* of value should be written to memory).
*
* NOTE: I have separated the immediate and PC-relative memory fetches
* from the other memory fetches because some systems require
* differentiation between PROGRAM and DATA fetches (usually
* for security setups such as encryption).
* This separation can either be achieved by setting
* M68K_SEPARATE_READS in m68kconf.h and defining
* the read functions, or by setting M68K_EMULATE_FC and
* making a function code callback function.
* Using the callback offers better emulation coverage
* because you can also monitor whether the CPU is in SYSTEM or
* USER mode, but it is also slower.
*/
/* Read from anywhere */
unsigned int m68k_read_memory_8(unsigned int address);
unsigned int m68k_read_memory_16(unsigned int address);
unsigned int m68k_read_memory_32(unsigned int address);
/* Read data immediately following the PC */
unsigned int m68k_read_immediate_16(unsigned int address);
unsigned int m68k_read_immediate_32(unsigned int address);
/* Read data relative to the PC */
unsigned int m68k_read_pcrelative_8(unsigned int address);
unsigned int m68k_read_pcrelative_16(unsigned int address);
unsigned int m68k_read_pcrelative_32(unsigned int address);
/* Memory access for the disassembler */
unsigned int m68k_read_disassembler_8 (unsigned int address);
unsigned int m68k_read_disassembler_16 (unsigned int address);
unsigned int m68k_read_disassembler_32 (unsigned int address);
/* Write to anywhere */
void m68k_write_memory_8(unsigned int address, unsigned int value);
void m68k_write_memory_16(unsigned int address, unsigned int value);
void m68k_write_memory_32(unsigned int address, unsigned int value);
/* Special call to simulate undocumented 68k behavior when move.l with a
* predecrement destination mode is executed.
* To simulate real 68k behavior, first write the high word to
* [address+2], and then write the low word to [address].
*
* Enable this functionality with M68K_SIMULATE_PD_WRITES in m68kconf.h.
*/
void m68k_write_memory_32_pd(unsigned int address, unsigned int value);
/* ======================================================================== */
/* ============================== CALLBACKS =============================== */
/* ======================================================================== */
/* These functions allow you to set callbacks to the host when specific events
* occur. Note that you must enable the corresponding value in m68kconf.h
* in order for these to do anything useful.
* Note: I have defined default callbacks which are used if you have enabled
* the corresponding #define in m68kconf.h but either haven't assigned a
* callback or have assigned a callback of NULL.
*/
/* Set the callback for an interrupt acknowledge.
* You must enable M68K_EMULATE_INT_ACK in m68kconf.h.
* The CPU will call the callback with the interrupt level being acknowledged.
* The host program must return either a vector from 0x02-0xff, or one of the
* special interrupt acknowledge values specified earlier in this header.
* If this is not implemented, the CPU will always assume an autovectored
* interrupt, and will automatically clear the interrupt request when it
* services the interrupt.
* Default behavior: return M68K_INT_ACK_AUTOVECTOR.
*/
void m68k_set_int_ack_callback(int (*callback)(int int_level));
/* Set the callback for a breakpoint acknowledge (68010+).
* You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h.
* The CPU will call the callback with whatever was in the data field of the
* BKPT instruction for 68020+, or 0 for 68010.
* Default behavior: do nothing.
*/
void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data));
/* Set the callback for the RESET instruction.
* You must enable M68K_EMULATE_RESET in m68kconf.h.
* The CPU calls this callback every time it encounters a RESET instruction.
* Default behavior: do nothing.
*/
void m68k_set_reset_instr_callback(void (*callback)(void));
/* Set the callback for the CMPI.L #v, Dn instruction.
* You must enable M68K_CMPILD_HAS_CALLBACK in m68kconf.h.
* The CPU calls this callback every time it encounters a CMPI.L #v, Dn instruction.
* Default behavior: do nothing.
*/
void m68k_set_cmpild_instr_callback(void (*callback)(unsigned int val, int reg));
/* Set the callback for the RTE instruction.
* You must enable M68K_RTE_HAS_CALLBACK in m68kconf.h.
* The CPU calls this callback every time it encounters a RTE instruction.
* Default behavior: do nothing.
*/
void m68k_set_rte_instr_callback(void (*callback)(void));
/* Set the callback for informing of a large PC change.
* You must enable M68K_MONITOR_PC in m68kconf.h.
* The CPU calls this callback with the new PC value every time the PC changes
* by a large value (currently set for changes by longwords).
* Default behavior: do nothing.
*/
void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc));
/* Set the callback for CPU function code changes.
* You must enable M68K_EMULATE_FC in m68kconf.h.
* The CPU calls this callback with the function code before every memory
* access to set the CPU's function code according to what kind of memory
* access it is (supervisor/user, program/data and such).
* Default behavior: do nothing.
*/
void m68k_set_fc_callback(void (*callback)(unsigned int new_fc));
/* Set a callback for the instruction cycle of the CPU.
* You must enable M68K_INSTRUCTION_HOOK in m68kconf.h.
* The CPU calls this callback just before fetching the opcode in the
* instruction cycle.
* Default behavior: do nothing.
*/
void m68k_set_instr_hook_callback(void (*callback)(void));
/* ======================================================================== */
/* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */
/* ======================================================================== */
/* Use this function to set the CPU type you want to emulate.
* Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68008,
* M68K_CPU_TYPE_68010, M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020.
*/
void m68k_set_cpu_type(unsigned int cpu_type);
/* Do whatever initialisations the core requires. Should be called
* at least once at init time.
*/
void m68k_init(void);
/* Pulse the RESET pin on the CPU.
* You *MUST* reset the CPU at least once to initialize the emulation
* Note: If you didn't call m68k_set_cpu_type() before resetting
* the CPU for the first time, the CPU will be set to
* M68K_CPU_TYPE_68000.
*/
void m68k_pulse_reset(void);
/* execute num_cycles worth of instructions. returns number of cycles used */
int m68k_execute(int num_cycles);
/* These functions let you read/write/modify the number of cycles left to run
* while m68k_execute() is running.
* These are useful if the 68k accesses a memory-mapped port on another device
* that requires immediate processing by another CPU.
*/
int m68k_cycles_run(void); /* Number of cycles run so far */
int m68k_cycles_remaining(void); /* Number of cycles left */
void m68k_modify_timeslice(int cycles); /* Modify cycles left */
void m68k_end_timeslice(void); /* End timeslice now */
/* Set the IPL0-IPL2 pins on the CPU (IRQ).
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
* Setting IRQ to 0 will clear an interrupt request.
*/
void m68k_set_irq(unsigned int int_level);
/* Halt the CPU as if you pulsed the HALT pin. */
void m68k_pulse_halt(void);
/* Context switching to allow multiple CPUs */
/* Get the size of the cpu context in bytes */
unsigned int m68k_context_size(void);
/* Get a cpu context */
unsigned int m68k_get_context(void* dst);
/* set the current cpu context */
void m68k_set_context(void* dst);
/* Register the CPU state information */
void m68k_state_register(const char *type);
/* Peek at the internals of a CPU context. This can either be a context
* retrieved using m68k_get_context() or the currently running context.
* If context is NULL, the currently running CPU context will be used.
*/
unsigned int m68k_get_reg(void* context, m68k_register_t reg);
/* Poke values into the internals of the currently running CPU context */
void m68k_set_reg(m68k_register_t reg, unsigned int value);
/* Check if an instruction is valid for the specified CPU type */
unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type);
/* Disassemble 1 instruction using the epecified CPU type at pc. Stores
* disassembly in str_buff and returns the size of the instruction in bytes.
*/
unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type);
/* ======================================================================== */
/* ============================= CONFIGURATION ============================ */
/* ======================================================================== */
/* Import the configuration for this build */
#include "m68kconf.h"
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */
#ifdef __cplusplus
}
#endif
#endif /* M68K__HEADER */
File diff suppressed because it is too large. Load diff
+254
View File
@@ -0,0 +1,254 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* m68kconf.h
*
* Musashi configuration.
*
* Permission was obtained from Karl Stenerud to apply the GPL license to this
* code.
*/
/* ======================================================================== */
/* ========================= LICENSING & COPYRIGHT ======================== */
/* ======================================================================== */
/*
* MUSASHI
* Version 3.3
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
*
* This code may be freely used for non-commercial purposes as long as this
* copyright notice remains unaltered in the source code and any binary files
* containing this code in compiled form.
*
* All other lisencing terms must be negotiated with the author
* (Karl Stenerud).
*
* The latest version of this code can be obtained at:
* http://kstenerud.cjb.net
*/
#ifndef M68KCONF__HEADER
#define M68KCONF__HEADER
#define M68K_COMPILE_FOR_MAME OPT_OFF
/* Configuration switches.
* Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks.
* OPT_SPECIFY_HANDLER causes the core to link directly to the function
* or macro you specify, rather than using callback functions whose pointer
* must be passed in using m68k_set_xxx_callback().
*/
#define OPT_OFF 0
#define OPT_ON 1
#define OPT_SPECIFY_HANDLER 2
/* ======================================================================== */
/* ============================= CONFIGURATION ============================ */
/* ======================================================================== */
/* Turn ON if you want to use the following M68K variants */
#define M68K_EMULATE_008 OPT_OFF
#define M68K_EMULATE_010 OPT_OFF
#define M68K_EMULATE_EC020 OPT_OFF
#define M68K_EMULATE_020 OPT_OFF
/* If ON, the CPU will call m68k_read_immediate_xx() for immediate addressing
* and m68k_read_pcrelative_xx() for PC-relative addressing.
* If off, all read requests from the CPU will be redirected to m68k_read_xx()
*/
#define M68K_SEPARATE_READS OPT_ON // fixes Supermodel compilation on MacOS
/* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a
* predecrement destination EA mode instead of m68k_write_32().
* To simulate real 68k behavior, m68k_write_32_pd() must first write the high
* word to [address+2], and then write the low word to [address].
*/
#define M68K_SIMULATE_PD_WRITES OPT_OFF
/* If ON, CPU will call the interrupt acknowledge callback when it services an
* interrupt.
* If off, all interrupts will be autovectored and all interrupt requests will
* auto-clear when the interrupt is serviced.
*/
#define M68K_EMULATE_INT_ACK OPT_SPECIFY_HANDLER
#define M68K_INT_ACK_CALLBACK(A) M68KIRQCallback(A)
/* If ON, CPU will call the breakpoint acknowledge callback when it encounters
* a breakpoint instruction and it is running a 68010+.
*/
#define M68K_EMULATE_BKPT_ACK OPT_OFF
#define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function()
/* If ON, the CPU will monitor the trace flags and take trace exceptions
*/
#define M68K_EMULATE_TRACE OPT_OFF
/* If ON, CPU will call the output reset callback when it encounters a reset
* instruction.
*/
#define M68K_EMULATE_RESET OPT_OFF
#define M68K_RESET_CALLBACK() M68KResetCallback()
/* If ON, CPU will call the callback when it encounters a cmpi.l #v, dn
* instruction.
*/
#define M68K_CMPILD_HAS_CALLBACK OPT_OFF
#define M68K_CMPILD_CALLBACK(v,r) your_cmpild_handler_function(v,r)
/* If ON, CPU will call the callback when it encounters a rte
* instruction.
*/
#define M68K_RTE_HAS_CALLBACK OPT_OFF
#define M68K_RTE_CALLBACK() your_rte_handler_function()
/* If ON, CPU will call the set fc callback on every memory access to
* differentiate between user/supervisor, program/data access like a real
* 68000 would. This should be enabled and the callback should be set if you
* want to properly emulate the m68010 or higher. (moves uses function codes
* to read/write data from different address spaces)
*/
#define M68K_EMULATE_FC OPT_OFF
#define M68K_SET_FC_CALLBACK(A) your_set_fc_handler_function(A)
/* If ON, CPU will call the pc changed callback when it changes the PC by a
* large value. This allows host programs to be nicer when it comes to
* fetching immediate data and instructions on a banked memory system.
*/
#define M68K_MONITOR_PC OPT_OFF
#define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A)
/* If ON, CPU will call the instruction hook callback before every
* instruction.
*/
#ifdef SUPERMODEL_DEBUGGER
#define M68K_INSTRUCTION_HOOK OPT_SPECIFY_HANDLER
#define M68K_INSTRUCTION_CALLBACK() M68KDebugCallback()
#else
#define M68K_INSTRUCTION_HOOK OPT_OFF
#define M68K_INSTRUCTION_CALLBACK() your_instruction_hook_function()
#endif // SUPERMODEL_DEBUGGER
/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */
#define M68K_EMULATE_PREFETCH OPT_OFF
/* If ON, the CPU will generate address error exceptions if it tries to
* access a word or longword at an odd address.
* NOTE: This is only emulated properly for 68000 mode.
*/
#define M68K_EMULATE_ADDRESS_ERROR OPT_OFF
/* Turn ON to enable logging of illegal instruction calls.
* M68K_LOG_FILEHANDLE must be #defined to a stdio file stream.
* Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls.
*/
#define M68K_LOG_ENABLE OPT_OFF
#define M68K_LOG_1010_1111 OPT_OFF
#define M68K_LOG_FILEHANDLE some_file_handle
/* ----------------------------- COMPATIBILITY ---------------------------- */
/* The following options set optimizations that violate the current ANSI
* standard, but will be compliant under the forthcoming C9X standard.
*/
/* If ON, the enulation core will use 64-bit integers to speed up some
* operations.
*/
#define M68K_USE_64_BIT OPT_ON
/* Set to your compiler's static inline keyword to enable it, or
* set it to blank to disable it.
* If you define INLINE in the makefile, it will override this value.
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
*/
#ifndef INLINE
#define INLINE static __inline__ // defined for GCC; if using MSVC, pass INLINE as "static __inline" from Makefile
#endif /* INLINE */
/******************************************************************************
Supermodel Interface
******************************************************************************/
// Supermodel 68K interface (these functions defined in CPU/68K.cpp)
//#ifndef FASTCALL (this doesn't work for now (needs to be added to the prototypes in m68k.h for m68k_read_memory*)
#undef FASTCALL
#define FASTCALL
//#endif
unsigned int FASTCALL M68KFetch8(unsigned int a);
unsigned int FASTCALL M68KFetch16(unsigned int a);
unsigned int FASTCALL M68KFetch32(unsigned int a);
unsigned int FASTCALL M68KRead8(unsigned int a);
unsigned int FASTCALL M68KRead16(unsigned int a);
unsigned int FASTCALL M68KRead32(unsigned int a);
void FASTCALL M68KWrite8(unsigned int a, unsigned int d);
void FASTCALL M68KWrite16(unsigned int a, unsigned int d);
void FASTCALL M68KWrite32(unsigned int a, unsigned int d);
/* Read data relative to the PC */
#define m68k_read_pcrelative_8(address) M68KFetch8(address)
#define m68k_read_pcrelative_16(address) M68KFetch16(address)
#define m68k_read_pcrelative_32(address) M68KFetch32(address)
/* Read data immediately following the PC */
#define m68k_read_immediate_16(address) M68KFetch16(address)
#define m68k_read_immediate_32(address) M68KFetch32(address)
/* Memory access for the disassembler */
#define m68k_read_disassembler_8(address) M68KRead8(address)
#define m68k_read_disassembler_16(address) M68KRead16(address)
#define m68k_read_disassembler_32(address) M68KRead32(address)
/* Read from anywhere */
#define m68k_read_memory_8(address) M68KRead8(address)
#define m68k_read_memory_16(address) M68KRead16(address)
#define m68k_read_memory_32(address) M68KRead32(address)
/* Write to anywhere */
#define m68k_write_memory_8(address, value) M68KWrite8(address, value)
#define m68k_write_memory_16(address, value) M68KWrite16(address, value)
#define m68k_write_memory_32(address, value) M68KWrite32(address, value)
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */
#endif /* M68KCONF__HEADER */
+957
View File
@@ -0,0 +1,957 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* m68kcpu.c
*
* Musashi core functions and interface.
*
* Permission was obtained from Karl Stenerud to apply the GPL license to this
* code.
*
* NOTE: The internal CPU context is initialized to all zeros here. If setting
* an external context for the first time, make sure it is cleared first,
* otherwise interrupts may appear pending and other nasty problems.
*/
/* ======================================================================== */
/* ========================= LICENSING & COPYRIGHT ======================== */
/* ======================================================================== */
#if 1
static const char* copyright_notice =
"MUSASHI\n"
"Version 3.3 (2001-01-29)\n"
"A portable Motorola M680x0 processor emulation engine.\n"
"Copyright 1998-2001 Karl Stenerud. All rights reserved.\n"
"\n"
"This code may be freely used for non-commercial purpooses as long as this\n"
"copyright notice remains unaltered in the source code and any binary files\n"
"containing this code in compiled form.\n"
"\n"
"All other lisencing terms must be negotiated with the author\n"
"(Karl Stenerud).\n"
"\n"
"The latest version of this code can be obtained at:\n"
"http://kstenerud.cjb.net\n"
;
#endif
/* ======================================================================== */
/* ================================= NOTES ================================ */
/* ======================================================================== */
/* ======================================================================== */
/* ================================ INCLUDES ============================== */
/* ======================================================================== */
#include "m68kops.h"
#include "m68kcpu.h"
/* ======================================================================== */
/* ================================= DATA ================================= */
/* ======================================================================== */
int m68ki_initial_cycles;
int m68ki_remaining_cycles = 0; /* Number of clocks remaining */
uint m68ki_tracing = 0;
uint m68ki_address_space;
#ifdef M68K_LOG_ENABLE
const char* m68ki_cpu_names[] =
{
"Invalid CPU",
"M68000",
"M68008",
"Invalid CPU",
"M68010",
"Invalid CPU",
"Invalid CPU",
"Invalid CPU",
"M68EC020",
"Invalid CPU",
"Invalid CPU",
"Invalid CPU",
"Invalid CPU",
"Invalid CPU",
"Invalid CPU",
"Invalid CPU",
"M68020"
};
#endif /* M68K_LOG_ENABLE */
/* The CPU core */
m68ki_cpu_core m68ki_cpu = {0};
#if M68K_EMULATE_ADDRESS_ERROR
jmp_buf m68ki_aerr_trap;
#endif /* M68K_EMULATE_ADDRESS_ERROR */
uint m68ki_aerr_address;
uint m68ki_aerr_write_mode;
uint m68ki_aerr_fc;
/* Used by shift & rotate instructions */
uint8 m68ki_shift_8_table[65] =
{
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff
};
uint16 m68ki_shift_16_table[65] =
{
0x0000, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff
};
uint m68ki_shift_32_table[65] =
{
0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000,
0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000,
0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000,
0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00,
0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8,
0xfffffffc, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
};
/* Number of clock cycles to use for exception processing.
* I used 4 for any vectors that are undocumented for processing times.
*/
uint8 m68ki_exception_cycle_table[3][256] =
{
{ /* 000 */
4, /* 0: Reset - Initial Stack Pointer */
4, /* 1: Reset - Initial Program Counter */
50, /* 2: Bus Error (unemulated) */
50, /* 3: Address Error (unemulated) */
34, /* 4: Illegal Instruction */
38, /* 5: Divide by Zero -- ASG: changed from 42 */
40, /* 6: CHK -- ASG: chanaged from 44 */
34, /* 7: TRAPV */
34, /* 8: Privilege Violation */
34, /* 9: Trace */
4, /* 10: 1010 */
4, /* 11: 1111 */
4, /* 12: RESERVED */
4, /* 13: Coprocessor Protocol Violation (unemulated) */
4, /* 14: Format Error */
44, /* 15: Uninitialized Interrupt */
4, /* 16: RESERVED */
4, /* 17: RESERVED */
4, /* 18: RESERVED */
4, /* 19: RESERVED */
4, /* 20: RESERVED */
4, /* 21: RESERVED */
4, /* 22: RESERVED */
4, /* 23: RESERVED */
44, /* 24: Spurious Interrupt */
44, /* 25: Level 1 Interrupt Autovector */
44, /* 26: Level 2 Interrupt Autovector */
44, /* 27: Level 3 Interrupt Autovector */
44, /* 28: Level 4 Interrupt Autovector */
44, /* 29: Level 5 Interrupt Autovector */
44, /* 30: Level 6 Interrupt Autovector */
44, /* 31: Level 7 Interrupt Autovector */
34, /* 32: TRAP #0 -- ASG: chanaged from 38 */
34, /* 33: TRAP #1 */
34, /* 34: TRAP #2 */
34, /* 35: TRAP #3 */
34, /* 36: TRAP #4 */
34, /* 37: TRAP #5 */
34, /* 38: TRAP #6 */
34, /* 39: TRAP #7 */
34, /* 40: TRAP #8 */
34, /* 41: TRAP #9 */
34, /* 42: TRAP #10 */
34, /* 43: TRAP #11 */
34, /* 44: TRAP #12 */
34, /* 45: TRAP #13 */
34, /* 46: TRAP #14 */
34, /* 47: TRAP #15 */
4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */
4, /* 49: FP Inexact Result (unemulated) */
4, /* 50: FP Divide by Zero (unemulated) */
4, /* 51: FP Underflow (unemulated) */
4, /* 52: FP Operand Error (unemulated) */
4, /* 53: FP Overflow (unemulated) */
4, /* 54: FP Signaling NAN (unemulated) */
4, /* 55: FP Unimplemented Data Type (unemulated) */
4, /* 56: MMU Configuration Error (unemulated) */
4, /* 57: MMU Illegal Operation Error (unemulated) */
4, /* 58: MMU Access Level Violation Error (unemulated) */
4, /* 59: RESERVED */
4, /* 60: RESERVED */
4, /* 61: RESERVED */
4, /* 62: RESERVED */
4, /* 63: RESERVED */
/* 64-255: User Defined */
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
},
{ /* 010 */
4, /* 0: Reset - Initial Stack Pointer */
4, /* 1: Reset - Initial Program Counter */
126, /* 2: Bus Error (unemulated) */
126, /* 3: Address Error (unemulated) */
38, /* 4: Illegal Instruction */
44, /* 5: Divide by Zero */
44, /* 6: CHK */
34, /* 7: TRAPV */
38, /* 8: Privilege Violation */
38, /* 9: Trace */
4, /* 10: 1010 */
4, /* 11: 1111 */
4, /* 12: RESERVED */
4, /* 13: Coprocessor Protocol Violation (unemulated) */
4, /* 14: Format Error */
44, /* 15: Uninitialized Interrupt */
4, /* 16: RESERVED */
4, /* 17: RESERVED */
4, /* 18: RESERVED */
4, /* 19: RESERVED */
4, /* 20: RESERVED */
4, /* 21: RESERVED */
4, /* 22: RESERVED */
4, /* 23: RESERVED */
46, /* 24: Spurious Interrupt */
46, /* 25: Level 1 Interrupt Autovector */
46, /* 26: Level 2 Interrupt Autovector */
46, /* 27: Level 3 Interrupt Autovector */
46, /* 28: Level 4 Interrupt Autovector */
46, /* 29: Level 5 Interrupt Autovector */
46, /* 30: Level 6 Interrupt Autovector */
46, /* 31: Level 7 Interrupt Autovector */
38, /* 32: TRAP #0 */
38, /* 33: TRAP #1 */
38, /* 34: TRAP #2 */
38, /* 35: TRAP #3 */
38, /* 36: TRAP #4 */
38, /* 37: TRAP #5 */
38, /* 38: TRAP #6 */
38, /* 39: TRAP #7 */
38, /* 40: TRAP #8 */
38, /* 41: TRAP #9 */
38, /* 42: TRAP #10 */
38, /* 43: TRAP #11 */
38, /* 44: TRAP #12 */
38, /* 45: TRAP #13 */
38, /* 46: TRAP #14 */
38, /* 47: TRAP #15 */
4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */
4, /* 49: FP Inexact Result (unemulated) */
4, /* 50: FP Divide by Zero (unemulated) */
4, /* 51: FP Underflow (unemulated) */
4, /* 52: FP Operand Error (unemulated) */
4, /* 53: FP Overflow (unemulated) */
4, /* 54: FP Signaling NAN (unemulated) */
4, /* 55: FP Unimplemented Data Type (unemulated) */
4, /* 56: MMU Configuration Error (unemulated) */
4, /* 57: MMU Illegal Operation Error (unemulated) */
4, /* 58: MMU Access Level Violation Error (unemulated) */
4, /* 59: RESERVED */
4, /* 60: RESERVED */
4, /* 61: RESERVED */
4, /* 62: RESERVED */
4, /* 63: RESERVED */
/* 64-255: User Defined */
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
},
{ /* 020 */
4, /* 0: Reset - Initial Stack Pointer */
4, /* 1: Reset - Initial Program Counter */
50, /* 2: Bus Error (unemulated) */
50, /* 3: Address Error (unemulated) */
20, /* 4: Illegal Instruction */
38, /* 5: Divide by Zero */
40, /* 6: CHK */
20, /* 7: TRAPV */
34, /* 8: Privilege Violation */
25, /* 9: Trace */
20, /* 10: 1010 */
20, /* 11: 1111 */
4, /* 12: RESERVED */
4, /* 13: Coprocessor Protocol Violation (unemulated) */
4, /* 14: Format Error */
30, /* 15: Uninitialized Interrupt */
4, /* 16: RESERVED */
4, /* 17: RESERVED */
4, /* 18: RESERVED */
4, /* 19: RESERVED */
4, /* 20: RESERVED */
4, /* 21: RESERVED */
4, /* 22: RESERVED */
4, /* 23: RESERVED */
30, /* 24: Spurious Interrupt */
30, /* 25: Level 1 Interrupt Autovector */
30, /* 26: Level 2 Interrupt Autovector */
30, /* 27: Level 3 Interrupt Autovector */
30, /* 28: Level 4 Interrupt Autovector */
30, /* 29: Level 5 Interrupt Autovector */
30, /* 30: Level 6 Interrupt Autovector */
30, /* 31: Level 7 Interrupt Autovector */
20, /* 32: TRAP #0 */
20, /* 33: TRAP #1 */
20, /* 34: TRAP #2 */
20, /* 35: TRAP #3 */
20, /* 36: TRAP #4 */
20, /* 37: TRAP #5 */
20, /* 38: TRAP #6 */
20, /* 39: TRAP #7 */
20, /* 40: TRAP #8 */
20, /* 41: TRAP #9 */
20, /* 42: TRAP #10 */
20, /* 43: TRAP #11 */
20, /* 44: TRAP #12 */
20, /* 45: TRAP #13 */
20, /* 46: TRAP #14 */
20, /* 47: TRAP #15 */
4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */
4, /* 49: FP Inexact Result (unemulated) */
4, /* 50: FP Divide by Zero (unemulated) */
4, /* 51: FP Underflow (unemulated) */
4, /* 52: FP Operand Error (unemulated) */
4, /* 53: FP Overflow (unemulated) */
4, /* 54: FP Signaling NAN (unemulated) */
4, /* 55: FP Unimplemented Data Type (unemulated) */
4, /* 56: MMU Configuration Error (unemulated) */
4, /* 57: MMU Illegal Operation Error (unemulated) */
4, /* 58: MMU Access Level Violation Error (unemulated) */
4, /* 59: RESERVED */
4, /* 60: RESERVED */
4, /* 61: RESERVED */
4, /* 62: RESERVED */
4, /* 63: RESERVED */
/* 64-255: User Defined */
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
}
};
uint8 m68ki_ea_idx_cycle_table[64] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, /* ..01.000 no memory indirect, base NULL */
5, /* ..01..01 memory indirect, base NULL, outer NULL */
7, /* ..01..10 memory indirect, base NULL, outer 16 */
7, /* ..01..11 memory indirect, base NULL, outer 32 */
0, 5, 7, 7, 0, 5, 7, 7, 0, 5, 7, 7,
2, /* ..10.000 no memory indirect, base 16 */
7, /* ..10..01 memory indirect, base 16, outer NULL */
9, /* ..10..10 memory indirect, base 16, outer 16 */
9, /* ..10..11 memory indirect, base 16, outer 32 */
0, 7, 9, 9, 0, 7, 9, 9, 0, 7, 9, 9,
6, /* ..11.000 no memory indirect, base 32 */
11, /* ..11..01 memory indirect, base 32, outer NULL */
13, /* ..11..10 memory indirect, base 32, outer 16 */
13, /* ..11..11 memory indirect, base 32, outer 32 */
0, 11, 13, 13, 0, 11, 13, 13, 0, 11, 13, 13
};
/* ======================================================================== */
/* =============================== CALLBACKS ============================== */
/* ======================================================================== */
/* Default callbacks used if the callback hasn't been set yet, or if the
* callback is set to NULL
*/
/* Interrupt acknowledge */
static int default_int_ack_callback_data;
static int default_int_ack_callback(int int_level)
{
default_int_ack_callback_data = int_level;
CPU_INT_LEVEL = 0;
return M68K_INT_ACK_AUTOVECTOR;
}
/* Breakpoint acknowledge */
static unsigned int default_bkpt_ack_callback_data;
static void default_bkpt_ack_callback(unsigned int data)
{
default_bkpt_ack_callback_data = data;
}
/* Called when a reset instruction is executed */
static void default_reset_instr_callback(void)
{
}
/* Called when a cmpi.l #v, dn instruction is executed */
static void default_cmpild_instr_callback(unsigned int val, int reg)
{
}
/* Called when a rte instruction is executed */
static void default_rte_instr_callback(void)
{
}
/* Called when the program counter changed by a large value */
static unsigned int default_pc_changed_callback_data;
static void default_pc_changed_callback(unsigned int new_pc)
{
default_pc_changed_callback_data = new_pc;
}
/* Called every time there's bus activity (read/write to/from memory */
static unsigned int default_set_fc_callback_data;
static void default_set_fc_callback(unsigned int new_fc)
{
default_set_fc_callback_data = new_fc;
}
/* Called every instruction cycle prior to execution */
static void default_instr_hook_callback(void)
{
}
#if M68K_EMULATE_ADDRESS_ERROR
#include <setjmp.h>
jmp_buf m68ki_aerr_trap;
#endif /* M68K_EMULATE_ADDRESS_ERROR */
/* ======================================================================== */
/* ================================= API ================================== */
/* ======================================================================== */
/* Access the internals of the CPU */
unsigned int m68k_get_reg(void* context, m68k_register_t regnum)
{
m68ki_cpu_core* cpu = context != NULL ?(m68ki_cpu_core*)context : &m68ki_cpu;
switch(regnum)
{
case M68K_REG_D0: return cpu->dar[0];
case M68K_REG_D1: return cpu->dar[1];
case M68K_REG_D2: return cpu->dar[2];
case M68K_REG_D3: return cpu->dar[3];
case M68K_REG_D4: return cpu->dar[4];
case M68K_REG_D5: return cpu->dar[5];
case M68K_REG_D6: return cpu->dar[6];
case M68K_REG_D7: return cpu->dar[7];
case M68K_REG_A0: return cpu->dar[8];
case M68K_REG_A1: return cpu->dar[9];
case M68K_REG_A2: return cpu->dar[10];
case M68K_REG_A3: return cpu->dar[11];
case M68K_REG_A4: return cpu->dar[12];
case M68K_REG_A5: return cpu->dar[13];
case M68K_REG_A6: return cpu->dar[14];
case M68K_REG_A7: return cpu->dar[15];
case M68K_REG_PC: return MASK_OUT_ABOVE_32(cpu->pc);
case M68K_REG_SR: return cpu->t1_flag |
cpu->t0_flag |
(cpu->s_flag << 11) |
(cpu->m_flag << 11) |
cpu->int_mask |
((cpu->x_flag & XFLAG_SET) >> 4) |
((cpu->n_flag & NFLAG_SET) >> 4) |
((!cpu->not_z_flag) << 2) |
((cpu->v_flag & VFLAG_SET) >> 6) |
((cpu->c_flag & CFLAG_SET) >> 8);
case M68K_REG_SP: return cpu->dar[15];
case M68K_REG_USP: return cpu->s_flag ? cpu->sp[0] : cpu->dar[15];
case M68K_REG_ISP: return cpu->s_flag && !cpu->m_flag ? cpu->dar[15] : cpu->sp[4];
case M68K_REG_MSP: return cpu->s_flag && cpu->m_flag ? cpu->dar[15] : cpu->sp[6];
case M68K_REG_SFC: return cpu->sfc;
case M68K_REG_DFC: return cpu->dfc;
case M68K_REG_VBR: return cpu->vbr;
case M68K_REG_CACR: return cpu->cacr;
case M68K_REG_CAAR: return cpu->caar;
case M68K_REG_PREF_ADDR: return cpu->pref_addr;
case M68K_REG_PREF_DATA: return cpu->pref_data;
case M68K_REG_PPC: return MASK_OUT_ABOVE_32(cpu->ppc);
case M68K_REG_IR: return cpu->ir;
case M68K_REG_CPU_TYPE:
switch(cpu->cpu_type)
{
case CPU_TYPE_000: return (unsigned int)M68K_CPU_TYPE_68000;
case CPU_TYPE_008: return (unsigned int)M68K_CPU_TYPE_68008;
case CPU_TYPE_010: return (unsigned int)M68K_CPU_TYPE_68010;
case CPU_TYPE_EC020: return (unsigned int)M68K_CPU_TYPE_68EC020;
case CPU_TYPE_020: return (unsigned int)M68K_CPU_TYPE_68020;
}
return M68K_CPU_TYPE_INVALID;
default: return 0;
}
return 0;
}
void m68k_set_reg(m68k_register_t regnum, unsigned int value)
{
switch(regnum)
{
case M68K_REG_D0: REG_D[0] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D1: REG_D[1] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D2: REG_D[2] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D3: REG_D[3] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D4: REG_D[4] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D5: REG_D[5] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D6: REG_D[6] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_D7: REG_D[7] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A0: REG_A[0] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A1: REG_A[1] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A2: REG_A[2] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A3: REG_A[3] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A4: REG_A[4] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A5: REG_A[5] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A6: REG_A[6] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A7: REG_A[7] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_PC: m68ki_jump(MASK_OUT_ABOVE_32(value)); return;
case M68K_REG_SR: m68ki_set_sr(value); return;
case M68K_REG_SP: REG_SP = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_USP: if(FLAG_S)
REG_USP = MASK_OUT_ABOVE_32(value);
else
REG_SP = MASK_OUT_ABOVE_32(value);
return;
case M68K_REG_ISP: if(FLAG_S && !FLAG_M)
REG_SP = MASK_OUT_ABOVE_32(value);
else
REG_ISP = MASK_OUT_ABOVE_32(value);
return;
case M68K_REG_MSP: if(FLAG_S && FLAG_M)
REG_SP = MASK_OUT_ABOVE_32(value);
else
REG_MSP = MASK_OUT_ABOVE_32(value);
return;
case M68K_REG_VBR: REG_VBR = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_SFC: REG_SFC = value & 7; return;
case M68K_REG_DFC: REG_DFC = value & 7; return;
case M68K_REG_CACR: REG_CACR = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_CAAR: REG_CAAR = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_PPC: REG_PPC = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_IR: REG_IR = MASK_OUT_ABOVE_16(value); return;
case M68K_REG_PREF_ADDR: CPU_PREF_ADDR = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_CPU_TYPE: m68k_set_cpu_type(value); return;
default: return;
}
}
/* Set the callbacks */
void m68k_set_int_ack_callback(int (*callback)(int int_level))
{
CALLBACK_INT_ACK = callback ? callback : default_int_ack_callback;
}
void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data))
{
CALLBACK_BKPT_ACK = callback ? callback : default_bkpt_ack_callback;
}
void m68k_set_reset_instr_callback(void (*callback)(void))
{
CALLBACK_RESET_INSTR = callback ? callback : default_reset_instr_callback;
}
void m68k_set_cmpild_instr_callback(void (*callback)(unsigned int, int))
{
CALLBACK_CMPILD_INSTR = callback ? callback : default_cmpild_instr_callback;
}
void m68k_set_rte_instr_callback(void (*callback)(void))
{
CALLBACK_RTE_INSTR = callback ? callback : default_rte_instr_callback;
}
void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc))
{
CALLBACK_PC_CHANGED = callback ? callback : default_pc_changed_callback;
}
void m68k_set_fc_callback(void (*callback)(unsigned int new_fc))
{
CALLBACK_SET_FC = callback ? callback : default_set_fc_callback;
}
void m68k_set_instr_hook_callback(void (*callback)(void))
{
CALLBACK_INSTR_HOOK = callback ? callback : default_instr_hook_callback;
}
#include <stdio.h>
/* Set the CPU type. */
void m68k_set_cpu_type(unsigned int cpu_type)
{
switch(cpu_type)
{
case M68K_CPU_TYPE_68000:
CPU_TYPE = CPU_TYPE_000;
CPU_ADDRESS_MASK = 0x00ffffff;
CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */
CYC_INSTRUCTION = m68ki_cycles[0];
CYC_EXCEPTION = m68ki_exception_cycle_table[0];
CYC_BCC_NOTAKE_B = -2;
CYC_BCC_NOTAKE_W = 2;
CYC_DBCC_F_NOEXP = -2;
CYC_DBCC_F_EXP = 2;
CYC_SCC_R_TRUE = 2;
CYC_MOVEM_W = 2;
CYC_MOVEM_L = 3;
CYC_SHIFT = 1;
CYC_RESET = 132;
return;
case M68K_CPU_TYPE_68008:
CPU_TYPE = CPU_TYPE_008;
CPU_ADDRESS_MASK = 0x003fffff;
CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */
CYC_INSTRUCTION = m68ki_cycles[0];
CYC_EXCEPTION = m68ki_exception_cycle_table[0];
CYC_BCC_NOTAKE_B = -2;
CYC_BCC_NOTAKE_W = 2;
CYC_DBCC_F_NOEXP = -2;
CYC_DBCC_F_EXP = 2;
CYC_SCC_R_TRUE = 2;
CYC_MOVEM_W = 2;
CYC_MOVEM_L = 3;
CYC_SHIFT = 1;
CYC_RESET = 132;
return;
case M68K_CPU_TYPE_68010:
CPU_TYPE = CPU_TYPE_010;
CPU_ADDRESS_MASK = 0x00ffffff;
CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */
CYC_INSTRUCTION = m68ki_cycles[1];
CYC_EXCEPTION = m68ki_exception_cycle_table[1];
CYC_BCC_NOTAKE_B = -4;
CYC_BCC_NOTAKE_W = 0;
CYC_DBCC_F_NOEXP = 0;
CYC_DBCC_F_EXP = 6;
CYC_SCC_R_TRUE = 0;
CYC_MOVEM_W = 2;
CYC_MOVEM_L = 3;
CYC_SHIFT = 1;
CYC_RESET = 130;
return;
case M68K_CPU_TYPE_68EC020:
CPU_TYPE = CPU_TYPE_EC020;
CPU_ADDRESS_MASK = 0x00ffffff;
CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
CYC_INSTRUCTION = m68ki_cycles[2];
CYC_EXCEPTION = m68ki_exception_cycle_table[2];
CYC_BCC_NOTAKE_B = -2;
CYC_BCC_NOTAKE_W = 0;
CYC_DBCC_F_NOEXP = 0;
CYC_DBCC_F_EXP = 4;
CYC_SCC_R_TRUE = 0;
CYC_MOVEM_W = 2;
CYC_MOVEM_L = 2;
CYC_SHIFT = 0;
CYC_RESET = 518;
return;
case M68K_CPU_TYPE_68020:
CPU_TYPE = CPU_TYPE_020;
CPU_ADDRESS_MASK = 0xffffffff;
CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
CYC_INSTRUCTION = m68ki_cycles[2];
CYC_EXCEPTION = m68ki_exception_cycle_table[2];
CYC_BCC_NOTAKE_B = -2;
CYC_BCC_NOTAKE_W = 0;
CYC_DBCC_F_NOEXP = 0;
CYC_DBCC_F_EXP = 4;
CYC_SCC_R_TRUE = 0;
CYC_MOVEM_W = 2;
CYC_MOVEM_L = 2;
CYC_SHIFT = 0;
CYC_RESET = 518;
return;
}
}
/* Execute some instructions until we use up num_cycles clock cycles */
/* ASG: removed per-instruction interrupt checks */
int m68k_execute(int num_cycles)
{
/* Make sure we're not stopped */
if(!CPU_STOPPED)
{
/* Set our pool of clock cycles available */
SET_CYCLES(num_cycles);
m68ki_initial_cycles = num_cycles;
/* ASG: update cycles */
USE_CYCLES(CPU_INT_CYCLES);
CPU_INT_CYCLES = 0;
/* Return point if we had an address error */
m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */
/* Main loop. Keep going until we run out of clock cycles */
do
{
/* Set tracing accodring to T1. (T0 is done inside instruction) */
m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */
/* Set the address space for reads */
m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */
/* Call external hook to peek at CPU */
m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */
/* Record previous program counter */
REG_PPC = REG_PC;
/* Read an instruction and call its handler */
REG_IR = m68ki_read_imm_16();
m68ki_instruction_jump_table[REG_IR]();
USE_CYCLES(CYC_INSTRUCTION[REG_IR]);
/* Trace m68k_exception, if necessary */
m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
} while(GET_CYCLES() > 0);
/* set previous PC to current PC for the next entry into the loop */
REG_PPC = REG_PC;
/* ASG: update cycles */
USE_CYCLES(CPU_INT_CYCLES);
CPU_INT_CYCLES = 0;
/* return how many clocks we used */
return m68ki_initial_cycles - GET_CYCLES();
}
/* We get here if the CPU is stopped or halted */
SET_CYCLES(0);
CPU_INT_CYCLES = 0;
return num_cycles;
}
int m68k_cycles_run(void)
{
return m68ki_initial_cycles - GET_CYCLES();
}
int m68k_cycles_remaining(void)
{
return GET_CYCLES();
}
/* Change the timeslice */
void m68k_modify_timeslice(int cycles)
{
m68ki_initial_cycles += cycles;
ADD_CYCLES(cycles);
}
void m68k_end_timeslice(void)
{
m68ki_initial_cycles -= GET_CYCLES();
SET_CYCLES(0);
}
/* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
/* KS: Modified so that IPL* bits match with mask positions in the SR
* and cleaned out remenants of the interrupt controller.
*/
void m68k_set_irq(unsigned int int_level)
{
uint old_level = CPU_INT_LEVEL;
CPU_INT_LEVEL = int_level << 8;
/* A transition from < 7 to 7 always interrupts (NMI) */
/* Note: Level 7 can also level trigger like a normal IRQ */
if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700)
m68ki_exception_interrupt(7); /* Edge triggered level 7 (NMI) */
else
m68ki_check_interrupts(); /* Level triggered (IRQ) */
}
void m68k_init(void)
{
static uint emulation_initialized = 0;
/* The first call to this function initializes the opcode handler jump table */
if(!emulation_initialized)
{
m68ki_build_opcode_table();
emulation_initialized = 1;
}
m68k_set_int_ack_callback(NULL);
m68k_set_bkpt_ack_callback(NULL);
m68k_set_reset_instr_callback(NULL);
m68k_set_cmpild_instr_callback(NULL);
m68k_set_rte_instr_callback(NULL);
m68k_set_pc_changed_callback(NULL);
m68k_set_fc_callback(NULL);
m68k_set_instr_hook_callback(NULL);
}
/* Pulse the RESET line on the CPU */
void m68k_pulse_reset(void)
{
/* Clear all stop levels and eat up all remaining cycles */
CPU_STOPPED = 0;
SET_CYCLES(0);
CPU_RUN_MODE = RUN_MODE_BERR_AERR_RESET;
/* Turn off tracing */
FLAG_T1 = FLAG_T0 = 0;
m68ki_clear_trace();
/* Interrupt mask to level 7 */
FLAG_INT_MASK = 0x0700;
/* Reset VBR */
REG_VBR = 0;
/* Go to supervisor mode */
m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR);
/* Invalidate the prefetch queue */
#if M68K_EMULATE_PREFETCH
/* Set to arbitrary number since our first fetch is from 0 */
CPU_PREF_ADDR = 0x1000;
#endif /* M68K_EMULATE_PREFETCH */
/* Read the initial stack pointer and program counter */
m68ki_jump(0);
REG_SP = m68ki_read_imm_32();
REG_PC = m68ki_read_imm_32();
m68ki_jump(REG_PC);
CPU_RUN_MODE = RUN_MODE_NORMAL;
}
/* Pulse the HALT line on the CPU */
void m68k_pulse_halt(void)
{
CPU_STOPPED |= STOP_LEVEL_HALT;
}
/* Get and set the current CPU context */
/* This is to allow for multiple CPUs */
unsigned int m68k_context_size()
{
return sizeof(m68ki_cpu_core);
}
unsigned int m68k_get_context(void* dst)
{
if(dst) *(m68ki_cpu_core*)dst = m68ki_cpu;
return sizeof(m68ki_cpu_core);
}
void m68k_set_context(void* src)
{
if(src) m68ki_cpu = *(m68ki_cpu_core*)src;
}
/* ======================================================================== */
/* ============================== MAME STUFF ============================== */
/* ======================================================================== */
#if M68K_COMPILE_FOR_MAME == OPT_ON
#include "state.h"
static struct {
UINT16 sr;
int stopped;
int halted;
} m68k_substate;
static void m68k_prepare_substate(void)
{
m68k_substate.sr = m68ki_get_sr();
m68k_substate.stopped = (CPU_STOPPED & STOP_LEVEL_STOP) != 0;
m68k_substate.halted = (CPU_STOPPED & STOP_LEVEL_HALT) != 0;
}
static void m68k_post_load(void)
{
m68ki_set_sr_noint_nosp(m68k_substate.sr);
CPU_STOPPED = m68k_substate.stopped ? STOP_LEVEL_STOP : 0
| m68k_substate.halted ? STOP_LEVEL_HALT : 0;
m68ki_jump(REG_PC);
}
void m68k_state_register(const char *type)
{
int cpu = cpu_getactivecpu();
state_save_register_UINT32(type, cpu, "D" , REG_D, 8);
state_save_register_UINT32(type, cpu, "A" , REG_A, 8);
state_save_register_UINT32(type, cpu, "PPC" , &REG_PPC, 1);
state_save_register_UINT32(type, cpu, "PC" , &REG_PC, 1);
state_save_register_UINT32(type, cpu, "USP" , &REG_USP, 1);
state_save_register_UINT32(type, cpu, "ISP" , &REG_ISP, 1);
state_save_register_UINT32(type, cpu, "MSP" , &REG_MSP, 1);
state_save_register_UINT32(type, cpu, "VBR" , &REG_VBR, 1);
state_save_register_UINT32(type, cpu, "SFC" , &REG_SFC, 1);
state_save_register_UINT32(type, cpu, "DFC" , &REG_DFC, 1);
state_save_register_UINT32(type, cpu, "CACR" , &REG_CACR, 1);
state_save_register_UINT32(type, cpu, "CAAR" , &REG_CAAR, 1);
state_save_register_UINT16(type, cpu, "SR" , &m68k_substate.sr, 1);
state_save_register_UINT32(type, cpu, "INT_LEVEL" , &CPU_INT_LEVEL, 1);
state_save_register_UINT32(type, cpu, "INT_CYCLES", &CPU_INT_CYCLES, 1);
state_save_register_int (type, cpu, "STOPPED" , &m68k_substate.stopped);
state_save_register_int (type, cpu, "HALTED" , &m68k_substate.halted);
state_save_register_UINT32(type, cpu, "PREF_ADDR" , &CPU_PREF_ADDR, 1);
state_save_register_UINT32(type, cpu, "PREF_DATA" , &CPU_PREF_DATA, 1);
state_save_register_func_presave(m68k_prepare_substate);
state_save_register_func_postload(m68k_post_load);
}
#endif /* M68K_COMPILE_FOR_MAME */
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */
File diff suppressed because it is too large. Load diff
Binary file not shown.
+87
View File
@@ -0,0 +1,87 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* m68kctx.h
*
* Musashi CPU context. This was made a separate file to more easily facilitate
* save state management. It is used internally by m68kcpu.h.
*/
#include "Types.h" // Supermodel types
typedef struct
{
UINT32 cpu_type; /* CPU Type: 68000, 68008, 68010, 68EC020, or 68020 */
UINT32 dar[16]; /* Data and Address Registers */
UINT32 ppc; /* Previous program counter */
UINT32 pc; /* Program Counter */
UINT32 sp[7]; /* User, Interrupt, and Master Stack Pointers */
UINT32 vbr; /* Vector Base Register (m68010+) */
UINT32 sfc; /* Source Function Code Register (m68010+) */
UINT32 dfc; /* Destination Function Code Register (m68010+) */
UINT32 cacr; /* Cache Control Register (m68020, unemulated) */
UINT32 caar; /* Cache Address Register (m68020, unemulated) */
UINT32 ir; /* Instruction Register */
UINT32 t1_flag; /* Trace 1 */
UINT32 t0_flag; /* Trace 0 */
UINT32 s_flag; /* Supervisor */
UINT32 m_flag; /* Master/Interrupt state */
UINT32 x_flag; /* Extend */
UINT32 n_flag; /* Negative */
UINT32 not_z_flag; /* Zero, inverted for speedups */
UINT32 v_flag; /* Overflow */
UINT32 c_flag; /* Carry */
UINT32 int_mask; /* I0-I2 */
UINT32 int_level; /* State of interrupt pins IPL0-IPL2 -- ASG: changed from ints_pending */
UINT32 int_cycles; /* ASG: extra cycles from generated interrupts */
UINT32 stopped; /* Stopped state */
UINT32 pref_addr; /* Last prefetch address */
UINT32 pref_data; /* Data in the prefetch queue */
UINT32 address_mask; /* Available address pins */
UINT32 sr_mask; /* Implemented status register bits */
UINT32 instr_mode; /* Stores whether we are in instruction mode or group 0/1 exception mode */
UINT32 run_mode; /* Stores whether we are processing a reset, bus error, address error, or something else */
/* Clocks required for instructions / exceptions */
UINT32 cyc_bcc_notake_b;
UINT32 cyc_bcc_notake_w;
UINT32 cyc_dbcc_f_noexp;
UINT32 cyc_dbcc_f_exp;
UINT32 cyc_scc_r_true;
UINT32 cyc_movem_w;
UINT32 cyc_movem_l;
UINT32 cyc_shift;
UINT32 cyc_reset;
UINT8* cyc_instruction;
UINT8* cyc_exception;
/* Callbacks to host */
int (*int_ack_callback)(int int_line); /* Interrupt Acknowledge */
void (*bkpt_ack_callback)(unsigned int data); /* Breakpoint Acknowledge */
void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */
void (*cmpild_instr_callback)(unsigned int, int); /* Called when a CMPI.L #v, Dn instruction is encountered */
void (*rte_instr_callback)(void); /* Called when a RTE instruction is encountered */
void (*pc_changed_callback)(unsigned int new_pc); /* Called when the PC changes by a large amount */
void (*set_fc_callback)(unsigned int new_fc); /* Called when the CPU function code changes */
void (*instr_hook_callback)(void); /* Called every instruction cycle prior to execution */
} m68ki_cpu_core;
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+181
View File
@@ -0,0 +1,181 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Turbo68K: Motorola 680X0 emulator
* Copyright 2000-2002 Bart Trzynadlowski, see "README.TXT" for terms of use
*/
#ifndef _TURBO68K_H_
#define _TURBO68K_H_
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************
* Generic Macros */
#define TURBO68K_OKAY 0 /* no error */
#define TURBO68K_NULL 0 /* null */
#define TURBO68K_SUPERVISOR 0 /* affects supervisor space */
#define TURBO68K_USER 1 /* function affects user space */
#define TURBO68K_UNINITIALIZED 15 /* uninitialized interrupt */
#define TURBO68K_SPURIOUS 24 /* spurious interrupt */
#define TURBO68K_AUTOVECTOR 256 /* autovectored interrupt */
/*****************************************************************************
* Error Codes */
#define TURBO68K_ERROR_FETCH 1 /* fetch error (PC bounds) */
#define TURBO68K_ERROR_INVINST 2 /* invalid instruction */
#define TURBO68K_ERROR_INTLEVEL 3 /* invalid interrupt level */
#define TURBO68K_ERROR_INTVECTOR 4 /* invalid interrupt vector */
#define TURBO68K_ERROR_INTPENDING 5 /* level already pending */
#define TURBO68K_ERROR_STACKFRAME 6 /* unhandled stack frame */
/*****************************************************************************
* Data Types */
typedef unsigned int TURBO68K_UINT32; /* unsigned 32-bit */
typedef signed int TURBO68K_INT32; /* signed 32-bit */
typedef unsigned short TURBO68K_UINT16; /* unsigned 16-bit */
typedef signed short TURBO68K_INT16; /* signed 16-bit */
typedef unsigned char TURBO68K_UINT8; /* unsigned 8-bit */
typedef signed char TURBO68K_INT8; /* signed 8-bit */
/*****************************************************************************
* Data Structures */
struct TURBO68K_FETCHREGION
{
TURBO68K_UINT32 base;
TURBO68K_UINT32 limit;
TURBO68K_UINT32 ptr;
};
struct TURBO68K_DATAREGION
{
TURBO68K_UINT32 base;
TURBO68K_UINT32 limit;
TURBO68K_UINT32 ptr;
void *handler;
};
struct TURBO68K_CONTEXT_68000
{
void *fetch, *pcfetch;
void *read_byte, *read_word, *read_long;
void *write_byte, *write_word, *write_long;
void *super_fetch, *super_pcfetch;
void *super_read_byte, *super_read_word, *super_read_long;
void *super_write_byte, *super_write_word, *super_write_long;
void *user_fetch, *user_pcfetch;
void *user_read_byte, *user_read_word, *user_read_long;
void *user_write_byte, *user_write_word, *user_write_long;
TURBO68K_UINT32 intr[8], cycles, remaining;
TURBO68K_UINT32 d[8], a[8], sp, sr, pc, status;
void *InterruptAcknowledge;
void *Reset;
void *Debug;
};
struct TURBO68K_CONTEXT_68010
{
void *fetch, *pcfetch;
void *read_byte, *read_word, *read_long;
void *write_byte, *write_word, *write_long;
void *super_fetch, *super_pcfetch;
void *super_read_byte, *super_read_word, *super_read_long;
void *super_write_byte, *super_write_word, *super_write_long;
void *user_fetch, *user_pcfetch;
void *user_read_byte, *user_read_word, *user_read_long;
void *user_write_byte, *user_write_word, *user_write_long;
TURBO68K_UINT32 intr[8], cycles, remaining;
TURBO68K_UINT32 d[8], a[8], sp, sr, pc, fc, vbr, status;
void *InterruptAcknowledge;
void *Reset, *Bkpt;
void *Debug;
};
/*****************************************************************************
* Functions and External Data */
#define TURBO68K_ID(ID) \
\
extern struct TURBO68K_CONTEXT_68000 ID##turbo68kcontext_68000; \
extern struct TURBO68K_CONTEXT_68010 ID##turbo68kcontext_68010; \
\
TURBO68K_INT32 ID##Turbo68KInit(); \
TURBO68K_INT32 ID##Turbo68KReset(); \
TURBO68K_INT32 ID##Turbo68KRun(TURBO68K_INT32); \
TURBO68K_INT32 ID##Turbo68KProcessInterrupts(); \
TURBO68K_INT32 ID##Turbo68KInterrupt(TURBO68K_INT32, TURBO68K_UINT32); \
TURBO68K_INT32 ID##Turbo68KCancelInterrupt(TURBO68K_INT32); \
\
TURBO68K_UINT32 ID##Turbo68KReadPC(); \
\
void ID##Turbo68KSetFetch(void *, TURBO68K_INT32); \
void ID##Turbo68KSetPCFetch(void *, TURBO68K_INT32); \
void ID##Turbo68KSetReadByte(void *, TURBO68K_INT32); \
void ID##Turbo68KSetReadWord(void *, TURBO68K_INT32); \
void ID##Turbo68KSetReadLong(void *, TURBO68K_INT32); \
void ID##Turbo68KSetWriteByte(void *, TURBO68K_INT32); \
void ID##Turbo68KSetWriteWord(void *, TURBO68K_INT32); \
void ID##Turbo68KSetWriteLong(void *, TURBO68K_INT32); \
void *ID##Turbo68KGetFetch(TURBO68K_INT32); \
void *ID##Turbo68KGetPCFetch(TURBO68K_INT32); \
void *ID##Turbo68KGetReadByte(TURBO68K_INT32); \
void *ID##Turbo68KGetReadWord(TURBO68K_INT32); \
void *ID##Turbo68KGetReadLong(TURBO68K_INT32); \
void *ID##Turbo68KGetWriteByte(TURBO68K_INT32); \
void *ID##Turbo68KGetWriteWord(TURBO68K_INT32); \
void *ID##Turbo68KGetWriteLong(TURBO68K_INT32); \
\
TURBO68K_UINT8 *ID##Turbo68KFetchPtr(TURBO68K_UINT32); \
TURBO68K_UINT8 ID##Turbo68KReadByte(TURBO68K_UINT32); \
TURBO68K_UINT16 ID##Turbo68KReadWord(TURBO68K_UINT32); \
TURBO68K_UINT32 ID##Turbo68KReadLong(TURBO68K_UINT32); \
void ID##Turbo68KWriteByte(TURBO68K_UINT32, TURBO68K_UINT8); \
void ID##Turbo68KWriteWord(TURBO68K_UINT32, TURBO68K_UINT16);\
void ID##Turbo68KWriteLong(TURBO68K_UINT32, TURBO68K_UINT32);\
\
void ID##Turbo68KSetContext(void *); \
void ID##Turbo68KGetContext(void *); \
TURBO68K_UINT32 ID##Turbo68KGetContextSize(); \
\
void ID##Turbo68KClearCycles(); \
void ID##Turbo68KFreeTimeSlice(); \
TURBO68K_INT32 ID##Turbo68KGetElapsedCycles(); \
TURBO68K_ID( ); /* default identifiers begin with nothing */
#ifdef __cplusplus
}
#endif
#endif /* _TURBO68K_H_ */
+109
View File
@@ -0,0 +1,109 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Bus.h
*
* Header file for the IBus abstract base class.
*/
#ifndef INCLUDED_BUS_H
#define INCLUDED_BUS_H
/*
* IBus:
*
* An interface for an address bus. Defines handlers for 8-, 16-, 32-, and 64-
* bit random access. Endianness should depend on the application.
*
* Two buses are supported: a memory bus and an IO bus. All addresses are 32
* bits.
*/
class IBus
{
public:
/*
* Read8(addr):
* Read16(addr):
* Read32(addr):
* Read64(addr):
*
* Read handlers.
*
* Parameters:
* addr Address (caller should ensure it is aligned to the correct
* boundary, corresponding to the size).
*
* Returns:
* Data of the appropriate size (8, 16, 32, or 64 bits).
*/
virtual UINT8 Read8(UINT32 addr) { return 0xFF; }
virtual UINT16 Read16(UINT32 addr) { return 0xFFFF; }
virtual UINT32 Read32(UINT32 addr) { return 0xFFFFFFFF; }
virtual UINT64 Read64(UINT32 addr) { return 0xFFFFFFFFFFFFFFFFULL; }
/*
* Write8(addr, data):
* Write16(addr, data):
* Write32(addr, data):
* Write64(addr, data):
*
* Write handlers.
*
* Parameters:
* addr Address (caller should ensure it is aligned to the correct
* boundary, corresponding to the size).
* data Data to write.
*/
virtual void Write8(UINT32 addr, UINT8 data) {}
virtual void Write16(UINT32 addr, UINT16 data) {}
virtual void Write32(UINT32 addr, UINT32 data) {}
virtual void Write64(UINT32 addr, UINT64 data) {}
/*
* IORead8(addr):
*
* Read handler for the IO bus. Only 8-bit data supported for now. Add more
* as needed.
*
* Parameters:
* addr Address.
*
* Returns:
* Data.
*/
virtual UINT8 IORead8(UINT32 addr) { return 0xFF; }
/*
* IOWrite8(addr, data):
*
* Write handler for the IO bus.
*
* Parameters:
* addr Address.
* data Data to write.
*/
virtual void IOWrite8(UINT32 addr, UINT8 data) {}
};
#endif // INCLUDED_BUS_H
File diff suppressed because it is too large. Load diff
+58
View File
@@ -0,0 +1,58 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* PPCDisasm.h
*
* Header file for PowerPC disassembler.
*/
#ifndef INCLUDED_PPCDISASM_H
#define INCLUDED_PPCDISASM_H
/*
* DisassemblePowerPC(op, vpc, mnem, oprs, simplify):
*
* Disassembles one PowerPC 603e instruction.
*
* A non-zero return code indicates that the instruction could not be
* recognized or that the operands to an instruction were invalid. To
* determine which case occured, check if mnem[0] == '\0'. If it does not,
* then the latter case happened.
*
* Arguments:
* op Instruction word to disassemble.
* vpc Current instruction address.
* mnem Buffer to write instruction mnemonic to. If no
* instruction was decoded, mnem[0] and oprs[0] will be set
* to '\0'.
* oprs Buffer to write any operands to.
* simplify If non-zero, simplified forms of instructions will be
* printed in certain cases.
*
* Returns:
* OKAY if successful, FAIL if the instruction was unrecognized or had an
* invalid form (see note above in function description.)
*/
extern bool DisassemblePowerPC(UINT32 op, UINT32 vpc, char *mnem, char *oprs,
bool simplify);
#endif // INCLUDED_PPCDISASM_H
Binary file not shown.
File diff suppressed because it is too large. Load diff
+385
View File
@@ -0,0 +1,385 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* ppc.h
*
* Header file for PowerPC emulator.
*/
#ifndef INCLUDED_PPC_H
#define INCLUDED_PPC_H
/******************************************************************************
Definitions
******************************************************************************/
#define SPR_XER 1 /* Fixed Point Exception Register Read / Write */
#define SPR_LR 8 /* Link Register Read / Write */
#define SPR_CTR 9 /* Count Register Read / Write */
#define SPR_SRR0 26 /* Save/Restore Register 0 Read / Write */
#define SPR_SRR1 27 /* Save/Restore Register 1 Read / Write */
#define SPR_SPRG0 272 /* SPR General 0 Read / Write */
#define SPR_SPRG1 273 /* SPR General 1 Read / Write */
#define SPR_SPRG2 274 /* SPR General 2 Read / Write */
#define SPR_SPRG3 275 /* SPR General 3 Read / Write */
#define SPR_PVR 287 /* Processor Version Number Read Only */
#define SPR403_ICDBDR 0x3D3 /* 406GA Instruction Cache Debug Data Register Rad Only */
#define SPR403_ESR 0x3D4 /* 406GA Exception Syndrome Register Read / Write */
#define SPR403_DEAR 0x3D5 /* 406GA Data Exception Address Register Read Only */
#define SPR403_EVPR 0x3D6 /* 406GA Exception Vector Prefix Register Read / Write */
#define SPR403_CDBCR 0x3D7 /* 406GA Cache Debug Control Register Read/Write */
#define SPR403_TSR 0x3D8 /* 406GA Timer Status Register Read / Clear */
#define SPR403_TCR 0x3DA /* 406GA Timer Control Register Read / Write */
#define SPR403_PIT 0x3DB /* 406GA Programmable Interval Timer Read / Write */
#define SPR403_TBHI 988 /* 406GA Time Base High Read / Write */
#define SPR403_TBLO 989 /* 406GA Time Base Low Read / Write */
#define SPR403_SRR2 0x3DE /* 406GA Save/Restore Register 2 Read / Write */
#define SPR403_SRR3 0x3DF /* 406GA Save/Restore Register 3 Read / Write */
#define SPR403_DBSR 0x3F0 /* 406GA Debug Status Register Read / Clear */
#define SPR403_DBCR 0x3F2 /* 406GA Debug Control Register Read / Write */
#define SPR403_IAC1 0x3F4 /* 406GA Instruction Address Compare 1 Read / Write */
#define SPR403_IAC2 0x3F5 /* 406GA Instruction Address Compare 2 Read / Write */
#define SPR403_DAC1 0x3F6 /* 406GA Data Address Compare 1 Read / Write */
#define SPR403_DAC2 0x3F7 /* 406GA Data Address Compare 2 Read / Write */
#define SPR403_DCCR 0x3FA /* 406GA Data Cache Cacheability Register Read / Write */
#define SPR403_ICCR 0x3FB /* 406GA I Cache Cacheability Registe Read / Write */
#define SPR403_PBL1 0x3FC /* 406GA Protection Bound Lower 1 Read / Write */
#define SPR403_PBU1 0x3FD /* 406GA Protection Bound Upper 1 Read / Write */
#define SPR403_PBL2 0x3FE /* 406GA Protection Bound Lower 2 Read / Write */
#define SPR403_PBU2 0x3FF /* 406GA Protection Bound Upper 2 Read / Write */
#define SPR403_SGR 0x3b9 /* 403GCX Storage Guarded Register */
#define SPR403_DCWR 0x3ba /* 403GCX Data Cache Write Through */
#define SPR403_PID 0x3b1 /* 403GCX Process ID */
#define SPR403_TBHU 0x3cc /* 403GCX Time Base High User-mode */
#define SPR403_TBLU 0x3cd /* 403GCX Time Base Low User-mode */
#define SPR603E_DSISR 18 /* 603E */
#define SPR603E_DAR 19 /* 603E */
#define SPR603E_DEC 22 /* 603E */
#define SPR603E_SDR1 25 /* 603E */
#define SPR603E_TBL_R 268 /* 603E Time Base Low (Read-only) */
#define SPR603E_TBU_R 269 /* 603E Time Base High (Read-only) */
#define SPR603E_TBL_W 284 /* 603E Time Base Low (Write-only) */
#define SPR603E_TBU_W 285 /* 603E Time Base Hight (Write-only) */
#define SPR603E_EAR 282 /* 603E */
#define SPR603E_IBAT0U 528 /* 603E */
#define SPR603E_IBAT0L 529 /* 603E */
#define SPR603E_IBAT1U 530 /* 603E */
#define SPR603E_IBAT1L 531 /* 603E */
#define SPR603E_IBAT2U 532 /* 603E */
#define SPR603E_IBAT2L 533 /* 603E */
#define SPR603E_IBAT3U 534 /* 603E */
#define SPR603E_IBAT3L 535 /* 603E */
#define SPR603E_DBAT0U 536 /* 603E */
#define SPR603E_DBAT0L 537 /* 603E */
#define SPR603E_DBAT1U 538 /* 603E */
#define SPR603E_DBAT1L 539 /* 603E */
#define SPR603E_DBAT2U 540 /* 603E */
#define SPR603E_DBAT2L 541 /* 603E */
#define SPR603E_DBAT3U 542 /* 603E */
#define SPR603E_DBAT3L 543 /* 603E */
#define SPR603E_DABR 1013 /* 603E */
#define SPR603E_DMISS 0x3d0 /* 603E */
#define SPR603E_DCMP 0x3d1 /* 603E */
#define SPR603E_HASH1 0x3d2 /* 603E */
#define SPR603E_HASH2 0x3d3 /* 603E */
#define SPR603E_IMISS 0x3d4 /* 603E */
#define SPR603E_ICMP 0x3d5 /* 603E */
#define SPR603E_RPA 0x3d6 /* 603E */
#define SPR603E_HID0 1008 /* 603E */
#define SPR603E_HID1 1009 /* 603E */
#define SPR603E_IABR 1010 /* 603E */
#define SPR603E_HID2 1011 /* 603E */
#define SPR602_TCR 984 /* 602 */
#define SPR602_IBR 986 /* 602 */
#define SPR602_SP 1021 /* 602 */
#define SPR602_LT 1022 /* 602 */
#define DCR_BEAR 0x90 /* bus error address */
#define DCR_BESR 0x91 /* bus error syndrome */
#define DCR_BR0 0x80 /* bank */
#define DCR_BR1 0x81 /* bank */
#define DCR_BR2 0x82 /* bank */
#define DCR_BR3 0x83 /* bank */
#define DCR_BR4 0x84 /* bank */
#define DCR_BR5 0x85 /* bank */
#define DCR_BR6 0x86 /* bank */
#define DCR_BR7 0x87 /* bank */
#define DCR_DMACC0 0xc4 /* dma chained count */
#define DCR_DMACC1 0xcc /* dma chained count */
#define DCR_DMACC2 0xd4 /* dma chained count */
#define DCR_DMACC3 0xdc /* dma chained count */
#define DCR_DMACR0 0xc0 /* dma channel control */
#define DCR_DMACR1 0xc8 /* dma channel control */
#define DCR_DMACR2 0xd0 /* dma channel control */
#define DCR_DMACR3 0xd8 /* dma channel control */
#define DCR_DMACT0 0xc1 /* dma destination address */
#define DCR_DMACT1 0xc9 /* dma destination address */
#define DCR_DMACT2 0xd1 /* dma destination address */
#define DCR_DMACT3 0xd9 /* dma destination address */
#define DCR_DMADA0 0xc2 /* dma destination address */
#define DCR_DMADA1 0xca /* dma destination address */
#define DCR_DMADA2 0xd2 /* dma source address */
#define DCR_DMADA3 0xda /* dma source address */
#define DCR_DMASA0 0xc3 /* dma source address */
#define DCR_DMASA1 0xcb /* dma source address */
#define DCR_DMASA2 0xd3 /* dma source address */
#define DCR_DMASA3 0xdb /* dma source address */
#define DCR_DMASR 0xe0 /* dma status */
#define DCR_EXIER 0x42 /* external interrupt enable */
#define DCR_EXISR 0x40 /* external interrupt status */
#define DCR_IOCR 0xa0 /* io configuration */
enum {
EXCEPTION_IRQ = 1,
EXCEPTION_DECREMENTER = 2,
EXCEPTION_TRAP = 3,
EXCEPTION_SYSTEM_CALL = 4,
EXCEPTION_SMI = 5,
EXCEPTION_DSI = 6,
EXCEPTION_ISI = 7,
EXCEPTION_PROGRAMMABLE_INTERVAL_TIMER = 20,
EXCEPTION_FIXED_INTERVAL_TIMER = 21,
EXCEPTION_WATCHDOG_TIMER = 22,
EXCEPTION_CRITICAL_INTERRUPT = 23,
};
enum {
PPC_INPUT_LINE_SMI = 10,
PPC_INPUT_LINE_TLBISYNC
};
enum {
PPC_PC=1,
PPC_MSR,
PPC_CR,
PPC_LR,
PPC_CTR,
PPC_XER,
PPC_DEC,
PPC_SRR0,
PPC_SRR1,
PPC_R0,
PPC_R1,
PPC_R2,
PPC_R3,
PPC_R4,
PPC_R5,
PPC_R6,
PPC_R7,
PPC_R8,
PPC_R9,
PPC_R10,
PPC_R11,
PPC_R12,
PPC_R13,
PPC_R14,
PPC_R15,
PPC_R16,
PPC_R17,
PPC_R18,
PPC_R19,
PPC_R20,
PPC_R21,
PPC_R22,
PPC_R23,
PPC_R24,
PPC_R25,
PPC_R26,
PPC_R27,
PPC_R28,
PPC_R29,
PPC_R30,
PPC_R31
};
typedef enum {
PPC_MODEL_403GA = 0x00200000,
PPC_MODEL_403GB = 0x00200100,
PPC_MODEL_403GC = 0x00200200,
PPC_MODEL_403GCX = 0x00201400,
PPC_MODEL_405GP = 0x40110000,
PPC_MODEL_601 = 0x00010000,
PPC_MODEL_603 = 0x00030000, /* "Wart" */
PPC_MODEL_604 = 0x00040000, /* "Zephyr" */
PPC_MODEL_602 = 0x00050000, /* "Galahad" */
PPC_MODEL_603E = 0x00060103, /* "Stretch", version 1.3 */
PPC_MODEL_603EV = 0x00070000, /* "Valiant" */
PPC_MODEL_603R = 0x00071202, /* "Goldeneye", version 2.1 */
PPC_MODEL_740 = 0x00080301, /* "Arthur", version 3.1 */
PPC_MODEL_750 = PPC_MODEL_740,
PPC_MODEL_740P = 0x00080202, /* "Conan Doyle", version 1.2 */
PPC_MODEL_750P = PPC_MODEL_740P,
PPC_MODEL_755 = 0x00083203 /* "Goldfinger", version 2.3 */
} PPC_MODEL;
typedef enum {
BUS_FREQUENCY_16MHZ = 0,
BUS_FREQUENCY_20MHZ,
BUS_FREQUENCY_25MHZ,
BUS_FREQUENCY_33MHZ,
BUS_FREQUENCY_40MHZ,
BUS_FREQUENCY_50MHZ,
BUS_FREQUENCY_60MHZ,
BUS_FREQUENCY_66MHZ,
BUS_FREQUENCY_75MHZ,
} PPC_BUS_FREQUENCY;
// PLL Configuration based on the table in MPC603EUM page 7-31
static const int mpc603e_pll_config[12][9] =
{
// 16, 20, 25, 33, 40, 50, 60, 66, 75
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, -1, 0x0, -1 },
{ -1, -1, -1, -1, -1, 0xc, -1, 0xc, -1 },
{ 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, -1, -1, -1 },
{ -1, -1, -1, 0x6, 0x6, -1, -1, -1, -1 },
{ -1, -1, 0x8, 0x8, -1, -1, -1, -1, -1 },
{ -1, 0xe, 0xe, -1, -1, -1, -1, -1, -1 },
{ 0xa, 0xa, 0xa, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
};
// PLL Configuration based on the table in MPC603E7VEC page 29
static const int mpc603ev_pll_config[12][9] =
{
// 16, 20, 25, 33, 40, 50, 60, 66, 75
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
// 2:1
{ -1, -1, -1, -1, -1, -1, -1, 0x4, 0x4 },
// 2.5:1
{ -1, -1, -1, -1, -1, 0x6, 0x6, 0x6, 0x6 },
// 3:1
{ -1, -1, -1, -1, 0x8, 0x8, 0x8, 0x8, 0x8 },
// 3.5:1
{ -1, -1, -1, -1, 0xe, 0xe, 0xe, 0xe, -1 },
// 4:1
{ -1, -1, -1, 0xa, 0xa, 0xa, 0xa, -1, -1 },
// 4.5:1
{ -1, -1, -1, 0x7, 0x7, 0x7, -1, -1, -1 },
// 5:1
{ -1, -1, 0xb, 0xb, 0xb, -1, -1, -1, -1 },
// 5.5:1
{ -1, -1, 0x9, 0x9, 0x9, -1, -1, -1, -1 },
// 6:1
{ -1, -1, 0xd, 0xd, 0xd, -1, -1, -1, -1 }
};
// PLL Configuration based on the table in MPC603E7TEC page 23
static const int mpc603r_pll_config[12][9] =
{
// 16, 20, 25, 33, 40, 50, 60, 66, 75
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
// 2:1
{ -1, -1, -1, -1, 0x5, 0x5, 0x5, 0x5, 0x5 },
// 2.5:1
{ -1, -1, -1, -1, -1, -1, 0x6, 0x6, 0x6 },
// 3:1
{ -1, -1, -1, -1, -1, 0x8, 0x8, 0x8, 0x8 },
// 3.5:1
{ -1, -1, -1, -1, -1, 0xe, 0xe, 0xe, 0xe },
// 4:1
{ -1, -1, -1, -1, 0xa, 0xa, 0xa, 0xa, 0xa },
// 4.5:1
{ -1, -1, -1, 0x7, 0x7, 0x7, 0x7, 0x7, -1 },
// 5:1
{ -1, -1, -1, 0xb, 0xb, 0xb, 0xb, -1, -1 },
// 5.5:1
{ -1, -1, -1, 0x9, 0x9, 0x9, -1, -1, -1 },
// 6:1
{ -1, -1, 0xd, 0xd, 0xd, 0xd, -1, -1, -1 },
};
/******************************************************************************
Configuration Data Structures
******************************************************************************/
typedef struct {
PPC_MODEL pvr;
int bus_frequency_multiplier;
PPC_BUS_FREQUENCY bus_frequency;
} PPC_CONFIG;
typedef struct
{
UINT32 start;
UINT32 end;
UINT32 * ptr;
} PPC_FETCH_REGION;
/******************************************************************************
Functions
******************************************************************************/
extern UINT32 ppc_get_pc(void);
extern void ppc_set_irq_line(int irqline);
extern int ppc_execute(int cycles);
extern void ppc_reset(void);
extern void ppc_shutdown(void);
extern void ppc_init(const PPC_CONFIG *config); // must be called second!
extern void ppc_set_fetch(PPC_FETCH_REGION * fetch);
extern UINT64 ppc_total_cycles(void);
extern int ppc_get_cycles_per_sec(void);
extern int ppc_get_bus_freq_multipler(void);
extern int ppc_get_timer_ratio(void);
extern void ppc_set_timer_ratio(int ratio);
// These have been added to support the new Supermodel
extern void ppc_attach_bus(class IBus *BusPtr); // must be called first!
extern void ppc_save_state(class CBlockFile *SaveState);
extern void ppc_load_state(class CBlockFile *SaveState);
extern UINT32 ppc_get_gpr(unsigned num);
extern double ppc_get_fpr(unsigned num);
extern UINT32 ppc_get_lr(void);
extern UINT32 ppc_read_spr(unsigned spr);
extern UINT32 ppc_read_sr(unsigned num);
#ifdef SUPERMODEL_DEBUGGER
// These have been added to support the Supermodel debugger
extern void ppc_attach_debugger(class Debugger::CPPCDebug *PPCDebugPtr);
extern void ppc_detach_debugger();
#endif // SUPERMODEL_DEBUGGER
extern void ppc_break();
extern void ppc_set_pc(UINT32 pc);
extern UINT8 ppc_get_cr(unsigned num);
extern void ppc_set_cr(unsigned num, UINT8 val);
extern void ppc_set_gpr(unsigned num, UINT32 val);
extern void ppc_set_fpr(unsigned num, double val);
extern void ppc_write_spr(unsigned spr, UINT32 val);
extern void ppc_write_sr(unsigned num, UINT32 val);
extern UINT32 ppc_read_msr();
#endif // INCLUDED_PPC_H
Binary file not shown.
+363
View File
@@ -0,0 +1,363 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* ppc603.c
*
* PowerPC 603e functions. Included from ppc.cpp; do not compile separately.
*/
void ppc603_exception(int exception)
{
#ifdef SUPERMODEL_DEBUGGER
if (PPCDebug != NULL)
PPCDebug->CPUException(exception);
#endif
switch( exception )
{
case EXCEPTION_IRQ: /* External Interrupt */
if( ppc_get_msr() & MSR_EE ) {
UINT32 msr = ppc_get_msr();
SRR0 = ppc.npc;
SRR1 = msr & 0xff73;
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x0500;
else
ppc.npc = 0x00000000 | 0x0500;
ppc.interrupt_pending &= ~0x1;
ppc_change_pc(ppc.npc);
}
break;
case EXCEPTION_DECREMENTER: /* Decrementer overflow exception */
if( ppc_get_msr() & MSR_EE ) {
UINT32 msr = ppc_get_msr();
SRR0 = ppc.npc;
SRR1 = msr & 0xff73;
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x0900;
else
ppc.npc = 0x00000000 | 0x0900;
ppc.interrupt_pending &= ~0x2;
ppc_change_pc(ppc.npc);
}
break;
case EXCEPTION_TRAP: /* Program exception / Trap */
{
UINT32 msr = ppc_get_msr();
SRR0 = ppc.pc;
SRR1 = (msr & 0xff73) | 0x20000; /* 0x20000 = TRAP bit */
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x0700;
else
ppc.npc = 0x00000000 | 0x0700;
ppc_change_pc(ppc.npc);
}
break;
case EXCEPTION_SYSTEM_CALL: /* System call */
{
UINT32 msr = ppc_get_msr();
SRR0 = ppc.npc;
SRR1 = (msr & 0xff73);
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x0c00;
else
ppc.npc = 0x00000000 | 0x0c00;
ppc_change_pc(ppc.npc);
}
break;
case EXCEPTION_SMI:
if( ppc_get_msr() & MSR_EE ) {
UINT32 msr = ppc_get_msr();
SRR0 = ppc.npc;
SRR1 = msr & 0xff73;
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x1400;
else
ppc.npc = 0x00000000 | 0x1400;
ppc.interrupt_pending &= ~0x4;
ppc_change_pc(ppc.npc);
}
break;
case EXCEPTION_DSI:
{
UINT32 msr = ppc_get_msr();
SRR0 = ppc.npc;
SRR1 = msr & 0xff73;
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x0300;
else
ppc.npc = 0x00000000 | 0x0300;
ppc.interrupt_pending &= ~0x4;
ppc_change_pc(ppc.npc);
}
break;
case EXCEPTION_ISI:
{
UINT32 msr = ppc_get_msr();
SRR0 = ppc.npc;
SRR1 = msr & 0xff73;
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
if( msr & MSR_ILE )
msr |= MSR_LE;
else
msr &= ~MSR_LE;
ppc_set_msr(msr);
if( msr & MSR_IP )
ppc.npc = 0xfff00000 | 0x0400;
else
ppc.npc = 0x00000000 | 0x0400;
ppc.interrupt_pending &= ~0x4;
ppc_change_pc(ppc.npc);
}
break;
default:
ErrorLog("PowerPC triggered an unknown exception. Emulation halted until reset.");
DebugLog("PowerPC triggered an unknown exception (%d).\n", exception);
ppc.fatalError = true;
break;
}
}
static void ppc603_set_smi_line(int state)
{
if( state ) {
ppc.interrupt_pending |= 0x4;
}
}
static void ppc603_check_interrupts(void)
{
if (MSR & MSR_EE)
{
if (ppc.interrupt_pending != 0)
{
if (ppc.interrupt_pending & 0x1)
{
ppc603_exception(EXCEPTION_IRQ);
}
else if (ppc.interrupt_pending & 0x2)
{
ppc603_exception(EXCEPTION_DECREMENTER);
}
else if (ppc.interrupt_pending & 0x4)
{
ppc603_exception(EXCEPTION_SMI);
}
}
}
}
void ppc_reset(void)
{
ppc.fatalError = false; // reset the fatal error flag
ppc.pc = ppc.npc = 0xfff00100;
ppc_set_msr(0x40);
ppc_change_pc(ppc.pc);
ppc.hid0 = 1;
ppc.interrupt_pending = 0;
ppc.tb = 0;
ppc.timer_frac = 0;
DEC = 0xffffffff;
ppc.total_cycles = 0;
ppc.cur_cycles = 0;
ppc.icount = 0;
}
int ppc_execute(int cycles)
{
UINT32 opcode;
ppc.cur_cycles = cycles;
ppc.icount = cycles;
ppc.tb_base_icount = cycles + ppc.timer_frac;
ppc.dec_base_icount = cycles + ppc.timer_frac;
// Check if decrementer exception occurs during execution (exception occurs after decrementer
// has passed through zero)
if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC)
ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio);
else
ppc.dec_trigger_cycle = 0x7fffffff;
ppc_change_pc(ppc.npc);
/*{
char string1[200];
char string2[200];
opcode = BSWAP32(*ppc.op);
DisassemblePowerPC(opcode, ppc.npc, string1, string2, true);
printf("%08X: %s %s\n", ppc.npc, string1, string2);
}*/
ppc603_check_interrupts();
#ifdef SUPERMODEL_DEBUGGER
if (PPCDebug != NULL)
PPCDebug->CPUActive();
#endif // SUPERMODEL_DEBUGGER
while( ppc.icount > 0 && !ppc.fatalError)
{
ppc.pc = ppc.npc;
// Debug breakpoints
/*
if (ppc.pc == 0x9d40)
{
printf("%X R3=%08X R4=%08X\n", ppc.pc, REG(3), REG(4));
}
*/
opcode = *ppc.op++; // Supermodel byte reverses each aligned word (converting them to little endian) so they can be fetched directly
ppc.npc = ppc.pc + 4;
#ifdef SUPERMODEL_DEBUGGER
if (PPCDebug != NULL)
{
while (PPCDebug->CPUExecute(ppc.pc, opcode, (PPCDebug->instrCount > 0 ? 1 : 0)))
opcode = *ppc.op++;
}
#endif // SUPERMODEL_DEBUGGER
switch(opcode >> 26)
{
case 19: optable19[(opcode >> 1) & 0x3ff](opcode); break;
case 31: optable31[(opcode >> 1) & 0x3ff](opcode); break;
case 59: optable59[(opcode >> 1) & 0x3ff](opcode); break;
case 63: optable63[(opcode >> 1) & 0x3ff](opcode); break;
default: optable[opcode >> 26](opcode); break;
}
ppc.icount--;
if (ppc.icount == ppc.dec_trigger_cycle)
{
ppc.interrupt_pending |= 0x2;
ppc603_check_interrupts();
}
//ppc603_check_interrupts();
}
#ifdef SUPERMODEL_DEBUGGER
if (PPCDebug != NULL)
PPCDebug->CPUInactive();
#endif // SUPERMODEL_DEBUGGER
// Update timebase and decrementer. Both are updated at same rate as specified by timer_ratio.
ppc.timer_frac = ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
ppc.tb += ((ppc.tb_base_icount - ppc.icount) / ppc.timer_ratio);
DEC -= ((ppc.dec_base_icount - ppc.icount) / ppc.timer_ratio);
/*
{
char string1[200];
char string2[200];
opcode = BSWAP32(*ppc.op);
DisassemblePowerPC(opcode, ppc.npc, string1, string2, true);
printf("%08X: %s %s\n", ppc.npc, string1, string2);
}
*/
int executed = cycles - ppc.icount;
ppc.total_cycles += executed;
ppc.cur_cycles = 0;
ppc.icount = 0;
ppc.tb_base_icount = 0;
ppc.dec_base_icount = 0;
return executed;
}
File diff suppressed because it is too large. Load diff
+170
View File
@@ -0,0 +1,170 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* ppc_ops.h
*
* Header file defining PowerPC opcode handlers.
*/
static PPC_OPCODE ppc_opcode_common[] =
{
/*code subcode handler */
{ 31, 266, ppc_addx },
{ 31, 266 | 512, ppc_addx },
{ 31, 10, ppc_addcx },
{ 31, 10 | 512, ppc_addcx },
{ 31, 138, ppc_addex },
{ 31, 138 | 512, ppc_addex },
{ 14, -1, ppc_addi },
{ 12, -1, ppc_addic },
{ 13, -1, ppc_addic_rc },
{ 15, -1, ppc_addis },
{ 31, 234, ppc_addmex },
{ 31, 234 | 512, ppc_addmex },
{ 31, 202, ppc_addzex },
{ 31, 202 | 512, ppc_addzex },
{ 31, 28, ppc_andx },
{ 31, 28 | 512, ppc_andx },
{ 31, 60, ppc_andcx },
{ 28, -1, ppc_andi_rc },
{ 29, -1, ppc_andis_rc },
{ 18, -1, ppc_bx },
{ 16, -1, ppc_bcx },
{ 19, 528, ppc_bcctrx },
{ 19, 16, ppc_bclrx },
{ 31, 0, ppc_cmp },
{ 11, -1, ppc_cmpi },
{ 31, 32, ppc_cmpl },
{ 10, -1, ppc_cmpli },
{ 31, 26, ppc_cntlzw },
{ 19, 257, ppc_crand },
{ 19, 129, ppc_crandc },
{ 19, 289, ppc_creqv },
{ 19, 225, ppc_crnand },
{ 19, 33, ppc_crnor },
{ 19, 449, ppc_cror },
{ 19, 417, ppc_crorc },
{ 19, 193, ppc_crxor },
{ 31, 86, ppc_dcbf },
{ 31, 470, ppc_dcbi },
{ 31, 54, ppc_dcbst },
{ 31, 278, ppc_dcbt },
{ 31, 246, ppc_dcbtst },
{ 31, 1014, ppc_dcbz },
{ 31, 491, ppc_divwx },
{ 31, 491 | 512, ppc_divwx },
{ 31, 459, ppc_divwux },
{ 31, 459 | 512, ppc_divwux },
{ 31, 854, ppc_eieio },
{ 31, 284, ppc_eqvx },
{ 31, 954, ppc_extsbx },
{ 31, 922, ppc_extshx },
{ 31, 982, ppc_icbi },
{ 19, 150, ppc_isync },
{ 34, -1, ppc_lbz },
{ 35, -1, ppc_lbzu },
{ 31, 119, ppc_lbzux },
{ 31, 87, ppc_lbzx },
{ 42, -1, ppc_lha },
{ 43, -1, ppc_lhau },
{ 31, 375, ppc_lhaux },
{ 31, 343, ppc_lhax },
{ 31, 790, ppc_lhbrx },
{ 40, -1, ppc_lhz },
{ 41, -1, ppc_lhzu },
{ 31, 311, ppc_lhzux },
{ 31, 279, ppc_lhzx },
{ 46, -1, ppc_lmw },
{ 31, 597, ppc_lswi },
{ 31, 533, ppc_lswx },
{ 31, 20, ppc_lwarx },
{ 31, 534, ppc_lwbrx },
{ 32, -1, ppc_lwz },
{ 33, -1, ppc_lwzu },
{ 31, 55, ppc_lwzux },
{ 31, 23, ppc_lwzx },
{ 19, 0, ppc_mcrf },
{ 31, 512, ppc_mcrxr },
{ 31, 19, ppc_mfcr },
{ 31, 83, ppc_mfmsr },
{ 31, 339, ppc_mfspr },
{ 31, 144, ppc_mtcrf },
{ 31, 146, ppc_mtmsr },
{ 31, 467, ppc_mtspr },
{ 31, 75, ppc_mulhwx },
{ 31, 11, ppc_mulhwux },
{ 7, -1, ppc_mulli },
{ 31, 235, ppc_mullwx },
{ 31, 235 | 512, ppc_mullwx },
{ 31, 476, ppc_nandx },
{ 31, 104, ppc_negx },
{ 31, 104 | 512, ppc_negx },
{ 31, 124, ppc_norx },
{ 31, 444, ppc_orx },
{ 31, 412, ppc_orcx },
{ 24, -1, ppc_ori },
{ 25, -1, ppc_oris },
{ 19, 50, ppc_rfi },
{ 20, -1, ppc_rlwimix },
{ 21, -1, ppc_rlwinmx },
{ 23, -1, ppc_rlwnmx },
{ 17, -1, ppc_sc },
{ 31, 24, ppc_slwx },
{ 31, 792, ppc_srawx },
{ 31, 824, ppc_srawix },
{ 31, 536, ppc_srwx },
{ 38, -1, ppc_stb },
{ 39, -1, ppc_stbu },
{ 31, 247, ppc_stbux },
{ 31, 215, ppc_stbx },
{ 44, -1, ppc_sth },
{ 31, 918, ppc_sthbrx },
{ 45, -1, ppc_sthu },
{ 31, 439, ppc_sthux },
{ 31, 407, ppc_sthx },
{ 47, -1, ppc_stmw },
{ 31, 725, ppc_stswi },
{ 31, 661, ppc_stswx },
{ 36, -1, ppc_stw },
{ 31, 662, ppc_stwbrx },
{ 31, 150, ppc_stwcx_rc },
{ 37, -1, ppc_stwu },
{ 31, 183, ppc_stwux },
{ 31, 151, ppc_stwx },
{ 31, 40, ppc_subfx },
{ 31, 40 | 512, ppc_subfx },
{ 31, 8, ppc_subfcx },
{ 31, 8 | 512, ppc_subfcx },
{ 31, 136, ppc_subfex },
{ 31, 136 | 512, ppc_subfex },
{ 8, -1, ppc_subfic },
{ 31, 232, ppc_subfmex },
{ 31, 232 | 512, ppc_subfmex },
{ 31, 200, ppc_subfzex },
{ 31, 200 | 512, ppc_subfzex },
{ 31, 598, ppc_sync },
{ 31, 4, ppc_tw },
{ 3, -1, ppc_twi },
{ 31, 316, ppc_xorx },
{ 26, -1, ppc_xori },
{ 27, -1, ppc_xoris }
};
+4015
View File
File diff suppressed because it is too large. Load diff
+339
View File
@@ -0,0 +1,339 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Z80.h
*
* Z80 emulator header file. Based on the Z80 instruction set simulator by
* Frank D. Cringle and taken from YAZE-AG by Andreas Gerlich.
*
* Known inaccuracies:
*
* - Interrupts are accepted immediately after EI instruction. In reality,
* interrupts become enabled after the instruction following EI. This
* could be implemented with a state machine in Run() if needed.
* This feature exists to prevent interrupts from occuring before a RETI
* instruction executes in the interrupt handler. Interrupt callbacks
* should take care to immediately clear the interrupt line, otherwise,
* an improperly timed interrupt may occur right after an EI instruction
* but before the service routine has a chance to return.
* - HALT instruction is not implemented (it just exits).
* - 16-bit words are read as two bytes but these reads may not occur in
* the exact same order as the real device. Needs to be checked.
*/
#ifndef INCLUDED_Z80_H
#define INCLUDED_Z80_H
#include "Types.h"
#include "CPU/Bus.h"
#include "BlockFile.h"
/*
* Special Return Codes for Interrupt Callbacks
*
* When the Z80 takes an interrupt in mode 0, it expects an instruction (up to
* 3 bytes) to be presented on the bus. Typically, an RST instruction is
* supplied, and this is the only supported behavior here. The interrupt
* callback must return one of these values when the Z80 is in mode 0.
*/
#define Z80_INT_RST_00 0xC7 // RST 0x00 (jumps to 0x0000)
#define Z80_INT_RST_08 0xCF // RST 0x08 (jumps to 0x0008)
#define Z80_INT_RST_10 0xD7 // RST 0x10 (...)
#define Z80_INT_RST_18 0xDF // RST 0x18
#define Z80_INT_RST_20 0xE7 // RST 0x20
#define Z80_INT_RST_28 0xEF // RST 0x28
#define Z80_INT_RST_30 0xF7 // RST 0x30
#define Z80_INT_RST_38 0xFF // RST 0x38
#ifdef SUPERMODEL_DEBUGGER
// Extra interrupt types
#define Z80_EX_NMI 0
#define Z80_IM1_IRQ 1
#define Z80_IM2_VECTOR 2
// 8-bit registers
#define Z80_REG8_IFF 0
#define Z80_REG8_IM 1
#define Z80_REG8_I 2
#define Z80_REG8_R 3
#define Z80_REG8_A 4
#define Z80_REG8_F 5
#define Z80_REG8_B 6
#define Z80_REG8_C 7
#define Z80_REG8_D 8
#define Z80_REG8_E 9
#define Z80_REG8_H 10
#define Z80_REG8_L 11
// 16-bit registers
#define Z80_REG16_SP 0
#define Z80_REG16_PC 1
#define Z80_REG16_IR 2
#define Z80_REG16_AF 3
#define Z80_REG16_BC 4
#define Z80_REG16_DE 5
#define Z80_REG16_HL 6
#define Z80_REG16_IX 7
#define Z80_REG16_IY 8
#define Z80_REG16_AF_ 9
#define Z80_REG16_BC_ 10
#define Z80_REG16_DE_ 11
#define Z80_REG16_HL_ 12
namespace Debugger {
class CZ80Debug;
}
#endif // SUPERMODEL_DEBUGGER
/*
* CZ80:
*
* A Z80 CPU.
*/
class CZ80
{
public:
/*
* Run(numCycles):
*
* Runs the Z80 for the specified number of instruction cycles.
*
* Parameters:
* numCycles Number of instruction cycles to execute.
*
* Returns:
* Number of instruction cycles actually executed.
*/
int Run(int numCycles);
/*
* TriggerNMI(void):
*
* Triggers a non-maskable interrupt. This is equivalent to a high-to-low
* transition on the NMI pin (NMI pin is triggered on falling edges). This
* may be called while the Z80 is running. NMIs are always higher priority
* than interrupts and are taken first.
*/
void TriggerNMI(void);
/*
* SetINT(state):
*
* Set or clear the interrupt request. This may be called from memory
* handlers while the Z80 is running and should be used by interrupt
* callbacks to clear interrupts.
*
* Parameters:
* state If TRUE, this is equivalent to /INT being asserted on the
* Z80 (INT line low, which triggers an interrupt). If FALSE,
* this deasserts /INT (INT line high, no interrupt pending).
*/
void SetINT(bool state);
/*
* GetPC(void):
*
* Returns the current PC value.
*
* Returns:
* Current value of PC register.
*/
UINT16 GetPC(void);
#ifdef SUPERMODEL_DEBUGGER
/*
* GetReg8(reg8):
*
* Reads an 8-bit register.
*
* Parameters:
* reg8 Register number (use Z80_REG8_* macros).
*
* Returns:
* Value of given 8-bit register.
*/
UINT8 GetReg8(unsigned reg8);
/*
* SetReg8(reg8, value):
*
* Sets value of given 8-bit register.
*
* Parameters:
* reg8 Register number.
* value Value to write.
*
* Returns:
* True if succeeded, false if invalid register.
*/
bool SetReg8(unsigned reg8, UINT8 value);
/*
* GetReg16(reg16, value):
*
* Reads a 16-bit register.
*
* Parameters:
* reg16 Register number (use Z80_REG16_* macros).
*
* Returns:
* Value of given 16-bit register.
*/
UINT16 GetReg16(unsigned reg16);
/*
* SetReg16(state):
*
* Sets value of given 16-bit register.
*
* Parameters:
* reg16 Register.
* value Value to write.
*
* Returns:
* True if succeeded, false if invalid register.
*/
bool SetReg16(unsigned reg16, UINT16 value);
#endif // SUPERMODEL_DEBUGGER
/*
* Reset(void):
*
* Resets the Z80, clearing all registers and pending interrupt requests.
*/
void Reset(void);
/*
* SaveState(StateFile, name):
*
* Saves the CPU state to the block file.
*
* Parameters:
* StateFile Block file.
* name Name of block to create (e.g. "Main Z80"), to facilitate
* multiple Z80 states in the same file.
*/
void SaveState(CBlockFile *StateFile, const char *name);
/*
* LoadState(StateFile, name):
*
* Loads the CPU state.
*
* Parameters:
* StateFile Block file.
* name Name of block to load.
*/
void LoadState(CBlockFile *StateFile, const char *name);
/*
* Init(BusPtr, INTF):
*
* One-time initialization of the Z80 emulator. Must be called first.
*
* A bus object must be supplied which will be used to handle all memory
* and IO accesses. The Read8, Write8, IORead8, and IOWrite8 members need
* to be defined. Addresses are automatically clamped to 16 bits for memory
* and 8 bits for IO accesses.
*
* An interrupt callback, which is called each time an interrupt occurs,
* should also be supplied. The interrupt callback should explicitly clear
* the INT status (using SetINT(FALSE)) and then return the appropriate
* vector depending on the interrupt mode that is used by the system.
*
* For mode 0, only Z80_INT_RST_* values are acceptable. For mode 1, the
* return value is discarded because the Z80 will always use vector 0x0038.
* In mode 2, an 8-bit value supplying the lower 8-bits of the interrupt
* vector address must be returned. Bit 0 will be ignored in this case (0
* is used).
*
* If a callback is not installed, the interrupt status will automatically
* be cleared and interrupts will not be taken when the Z80 is in a mode
* that requires vector data from the callback. In mode 1, the interrupt
* callback (if one is provided) should explicitly clear the interrupt. The
* value returned will be unused.
*
* Parameters:
* BusPtr Pointer to a bus object.
* INTF Pointer to callback function. The function accepts a
* a pointer to the Z80 object that received the interrupt.
*/
void Init(IBus *BusPtr, int (*INTF)(CZ80 *Z80));
#ifdef SUPERMODEL_DEBUGGER
/*
* AttachDebugger(DebugPtr):
*
* Attaches debugger to CPU.
*/
void AttachDebugger(Debugger::CZ80Debug *DebugPtr);
/*
* DetachDebugger():
*
* Detaches debugger from CPU.
*/
void DetachDebugger();
#endif // SUPERMODEL_DEBUGGER
/*
* CZ80(void):
* ~CZ80(void):
*
* Constructor and destructor.
*/
CZ80(void);
~CZ80(void);
private:
// Registers
struct GPR { // general purpose registers
UINT16 bc;
UINT16 de;
UINT16 hl;
} regs[2]; // two sets: primary (0) and alternate (1)
UINT16 af[2]; // AF (primary and alternate)
UINT16 ir; // interrupt vector (I) and memory refresh (R)
UINT16 ix; // index register X
UINT16 iy; // index register Y
UINT16 sp; // stack pointer
UINT16 pc; // program counter
UINT8 iff; // interrupt flip flops (bit 1: IFF2, 0: IFF1)
UINT8 im; // interrupt mode (0, 1, or 2 only)
int regs_sel; // active register set (primary or alternate)
int af_sel; // active AF
// Memory and IO bus
IBus *Bus;
// Interrupts
bool nmiTrigger;
bool intLine;
int (*INTCallback)(CZ80 *Z80);
#ifdef SUPERMODEL_DEBUGGER
int lastCycles;
Debugger::CZ80Debug *Debug;
#endif // SUPERMODEL_DEBUGGER
};
#endif // INCLUDED_Z80_H
BIN
View File
Binary file not shown.
+148
View File
@@ -0,0 +1,148 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* AddressTable.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "AddressTable.h"
#include <cstring>
#include <string>
namespace Debugger
{
CAddressRef::CAddressRef(CCPUDebug *refCPU, UINT32 refAddr, UINT32 refSize) :
cpu(refCPU), addr(refAddr), size(refSize), addrEnd(refAddr + refSize - 1)
{
//
}
CAddressTable::CAddressTable() : m_count(0)
{
memset(m_tables, NULL, sizeof(m_tables));
}
CAddressTable::~CAddressTable()
{
Clear();
}
CAddressRef **CAddressTable::GetTableCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable)
{
// Convert address to table index
tableIndex = addr >> INDEX_SHIFT;
// See if have a table at index
if (m_tables[tableIndex] == NULL)
{
// If not, create one now and increase count
m_tables[tableIndex] = new CAddressRef*[TABLE_SIZE];
memset(m_tables[tableIndex], NULL, sizeof(CAddressRef*) * TABLE_SIZE);
m_count++;
}
// Calculate index within table
indexInTable = addr & TABLE_MASK;
return m_tables[tableIndex];
}
void CAddressTable::CheckAndReleaseTable(int tableIndex)
{
// See if have table at give index
CAddressRef **table = m_tables[tableIndex];
if (table == NULL)
return;
// If so, check if it is empty
for (int i = 0; i < TABLE_SIZE; i++)
{
if (table[i] != NULL)
return;
}
// If so, delete it and decrease count
delete[] table;
m_tables[tableIndex] = NULL;
m_count--;
}
bool CAddressTable::IsEmpty()
{
return m_count == 0;
}
void CAddressTable::Clear()
{
if (m_count == 0)
return;
// Delete all tables and reset count
for (int i = 0; i < NUM_TABLES; i++)
{
if (m_tables[i] != NULL)
{
delete[] m_tables[i];
m_tables[i] = NULL;
}
}
m_count = 0;
}
void CAddressTable::Add(CAddressRef *value)
{
int tableIndex;
unsigned indexInTable;
CAddressRef **table = GetTableCreate(value->addr, tableIndex, indexInTable);
for (UINT32 i = 0; i < value->size; i++)
{
table[indexInTable] = value;
if (indexInTable >= TABLE_SIZE - 1)
table = GetTableCreate(value->addr + i, tableIndex, indexInTable);
else
indexInTable++;
}
}
bool CAddressTable::Remove(CAddressRef *value)
{
int tableIndex;
unsigned indexInTable;
CAddressRef **table = GetTableNoCreate(value->addr, tableIndex, indexInTable);
bool removed = false;
for (UINT32 i = 0; i < value->size; i++)
{
if (table != NULL)
{
removed |= table[indexInTable] != NULL;
table[indexInTable] = NULL;
}
if (indexInTable >= TABLE_SIZE - 1)
{
CheckAndReleaseTable(tableIndex);
table = GetTableNoCreate(value->addr + i, tableIndex, indexInTable);
}
else
indexInTable++;
}
CheckAndReleaseTable(tableIndex);
return removed;
}
}
#endif // SUPERMODEL_DEBUGGER
+155
View File
@@ -0,0 +1,155 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* AddressTable.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_ADDRESSTABLE_H
#define INCLUDED_ADDRESSTABLE_H
#include "Types.h"
#include <vector>
#include <algorithm>
#define TABLE_WIDTH 16
#define TABLE_SIZE (1 << TABLE_WIDTH)
#define TABLE_MASK (TABLE_SIZE - 1)
#define INDEX_SHIFT TABLE_WIDTH
#define NUM_TABLES (0x100000000ULL / TABLE_SIZE)
namespace Debugger
{
class CCPUDebug;
/*
* Base class for objects that reference an address, such as a label or a comment.
*/
class CAddressRef
{
protected:
CAddressRef(CCPUDebug *refCPU, UINT32 refAddr, UINT32 refSize = 1);
public:
CCPUDebug *cpu;
const UINT32 addr;
const UINT32 size;
const UINT32 addrEnd;
bool CheckAddr(UINT32 loc);
// TODO - implement operators
};
/*
* Class that holds a table of address references. It does so in an a memory-expensive manner but with the result that it
* performs better (hopefully!) as lookups are constant-time.
*/
class CAddressTable
{
private:
int m_count;
CAddressRef **m_tables[NUM_TABLES];
CAddressRef **GetTableNoCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable);
CAddressRef **GetTableCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable);
void CheckAndReleaseTable(int tableIndex);
public:
CAddressTable();
~CAddressTable();
bool IsEmpty();
void Clear();
CAddressRef *Get(UINT32 addr);
CAddressRef *Get(UINT32 addr, UINT32 size);
void Add(CAddressRef *value);
bool Remove(CAddressRef *value);
};
//
// Inlined methods
//
inline bool CAddressRef::CheckAddr(UINT32 loc)
{
return addr <= loc && loc <= addrEnd;
}
inline CAddressRef **CAddressTable::GetTableNoCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable)
{
tableIndex = addr >> INDEX_SHIFT;
if (m_tables[tableIndex] == NULL)
return NULL;
indexInTable = addr & TABLE_MASK;
return m_tables[tableIndex];
}
inline CAddressRef *CAddressTable::Get(UINT32 addr)
{
int tableIndex;
unsigned indexInTable;
CAddressRef **table = GetTableNoCreate(addr, tableIndex, indexInTable);
if (table == NULL)
return NULL;
return table[indexInTable];
}
inline CAddressRef *CAddressTable::Get(UINT32 addr, UINT32 size)
{
int tableIndex;
unsigned indexInTable;
CAddressRef **table = GetTableNoCreate(addr, tableIndex, indexInTable);
for (UINT32 i = 0; i < size; i++)
{
if (table != NULL)
{
CAddressRef *ref = table[indexInTable];
if (ref != NULL)
return ref;
addr++;
if (indexInTable >= TABLE_SIZE - 1)
table = GetTableNoCreate(addr, tableIndex, indexInTable);
else
indexInTable++;
}
else
{
addr++;
table = GetTableNoCreate(addr, tableIndex, indexInTable);
}
}
return NULL;
}
}
#endif // INCLUDED_ADDRESSTABLE_H
#endif // SUPERMODEL_DEBUGGER
+112
View File
@@ -0,0 +1,112 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Breakpoint.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "CPUDebug.h"
#include "Breakpoint.h"
#include <cstdio>
namespace Debugger
{
CBreakpoint::CBreakpoint(CCPUDebug *bpCPU, int bpAddr, char bpSymbol, const char *bpType) :
CAddressRef(bpCPU, bpAddr), symbol(bpSymbol), type(bpType), active(true)
{
//
}
bool CBreakpoint::Check(UINT32 pc, UINT32 opcode)
{
return CheckAddr(pc) && active && CheckBreak(pc, opcode);
}
void CBreakpoint::Reset()
{
//
}
CSimpleBreakpoint::CSimpleBreakpoint(CCPUDebug *bpCPU, int bpAddr) : CBreakpoint(bpCPU, bpAddr, '*', "simple")
{
//
}
bool CSimpleBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode)
{
return true;
}
bool CSimpleBreakpoint::GetInfo(char *str)
{
return false;
}
CCountBreakpoint::CCountBreakpoint(CCPUDebug *bpCPU, int bpAddr, int count) : CBreakpoint(bpCPU, bpAddr, '+', "count"),
m_count(count), m_counter(0)
{
//
}
bool CCountBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode)
{
return ++m_counter == m_count;
}
void CCountBreakpoint::Reset()
{
m_counter = 0;
}
bool CCountBreakpoint::GetInfo(char *str)
{
sprintf(str, "%d / %d", m_counter, m_count);
return true;
}
CPrintBreakpoint::CPrintBreakpoint(CCPUDebug *bpCPU, int bpAddr) : CBreakpoint(bpCPU, bpAddr, 'P', "print")
{
//
}
bool CPrintBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode)
{
char addrStr[50];
cpu->FormatAddress(addrStr, addr, true);
cpu->debugger->PrintEvent(cpu, "Breakpoint #%u hit at %s.\n", num, addrStr);
return false;
}
void CPrintBreakpoint::Reset()
{
//
}
bool CPrintBreakpoint::GetInfo(char *str)
{
return false;
}
}
#endif // SUPERMODEL_DEBUGGER
+113
View File
@@ -0,0 +1,113 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Breakpoint.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_BREAKPOINT_H
#define INCLUDED_BREAKPOINT_H
#include "AddressTable.h"
#include "Types.h"
namespace Debugger
{
class CCPUDebug;
/*
* Base class for a breakpoint.
*/
class CBreakpoint : public CAddressRef
{
protected:
CBreakpoint(CCPUDebug *bpCPU, int bpAddr, char bpSymbol, const char *bpType);
public:
const char symbol;
const char *type;
unsigned num;
bool active;
bool Check(UINT32 pc, UINT32 opcode);
virtual bool CheckBreak(UINT32 pc, UINT32 opcode) = 0;
virtual void Reset();
virtual bool GetInfo(char *str) = 0;
};
/*
* Simple breakpoint that will always halt execution when hit.
*/
class CSimpleBreakpoint : public CBreakpoint
{
public:
CSimpleBreakpoint(CCPUDebug *bpCPU, int bpAddr);
bool CheckBreak(UINT32 pc, UINT32 opcode);
bool GetInfo(char *str);
};
/*
* Count breakpoint that will halt execution after it has been hit a certain number of times.
*/
class CCountBreakpoint : public CBreakpoint
{
private:
int m_count;
int m_counter;
public:
CCountBreakpoint(CCPUDebug *bpCPU, int bpAddr, int count);
bool CheckBreak(UINT32 pc, UINT32 opcode);
void Reset();
bool GetInfo(char *str);
};
class CPrintBreakpoint : public CBreakpoint
{
public:
CPrintBreakpoint(CCPUDebug *bpCPU, int bpAddr);
bool CheckBreak(UINT32 pc, UINT32 opcode);
void Reset();
bool GetInfo(char *str);
};
//class CConditionBreakpoint : public CBrekapoint
//{
// // TODO
//}
}
#endif // INCLUDED_BREAKPOINT_H
#endif // SUPERMODEL_DEBUGGER
File diff suppressed because it is too large. Load diff
+68
View File
@@ -0,0 +1,68 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* 68KDebug.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_68KDEBUG_H
#define INCLUDED_68KDEBUG_H
#include "Debugger/CPUDebug.h"
#include "Types.h"
namespace Debugger
{
/*
* Base class CCPUDebug implementation for Motorola 68000 CPUs.
*/
class C68KDebug : public CCPUDebug
{
private:
bool FormatAddrMode(UINT32 addr, UINT32 opcode, int &offset, UINT8 addrMode, char sizeC, char *dest);
protected:
virtual UINT32 GetSP() = 0;
public:
C68KDebug(const char *name);
// CCPUDebug methods
int Disassemble(UINT32 addr, char *mnemonic, char *operands);
EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode);
bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr);
bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr);
bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr);
bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr);
bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr);
};
}
#endif // INCLUDED_68KDEBUG_H
#endif // SUPERMODEL_DEBUGGER
+203
View File
@@ -0,0 +1,203 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Musashi68KDebug.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Musashi68KDebug.h"
#include <cctype>
#include <string>
namespace Debugger
{
UINT32 CMusashi68KDebug::GetReg(CCPUDebug *cpu, unsigned id)
{
CMusashi68KDebug *m68K = (CMusashi68KDebug*)cpu;
return M68KGetRegister(m68K->m_ctx, id);
}
bool CMusashi68KDebug::SetReg(CCPUDebug *cpu, unsigned id, UINT32 data)
{
CMusashi68KDebug *m68K = (CMusashi68KDebug*)cpu;
m68K->SetM68KContext();
bool okay = M68KSetRegister(m68K->m_ctx, id, data);
m68K->RestoreM68KContext();
return okay;
}
static const char *srGroup = "Special Registers";
static const char *drGroup = "Data Registers";
static const char *arGroup = "Address Regsters";
CMusashi68KDebug::CMusashi68KDebug(const char *name, M68KCtx *ctx) : C68KDebug(name), m_ctx(ctx), m_resetAddr(0)
{
// Special registers
AddPCRegister ("PC", srGroup);
AddAddrRegister ("SP", srGroup, DBG68K_REG_SP, GetReg, SetReg);
AddStatus32Register("SR", srGroup, DBG68K_REG_SR, "TtSM.210...XNZVC", GetReg, SetReg);
// Data registers
AddInt32Register("D0", drGroup, DBG68K_REG_D0, GetReg, SetReg);
AddInt32Register("D1", drGroup, DBG68K_REG_D1, GetReg, SetReg);
AddInt32Register("D2", drGroup, DBG68K_REG_D2, GetReg, SetReg);
AddInt32Register("D3", drGroup, DBG68K_REG_D3, GetReg, SetReg);
AddInt32Register("D4", drGroup, DBG68K_REG_D4, GetReg, SetReg);
AddInt32Register("D5", drGroup, DBG68K_REG_D5, GetReg, SetReg);
AddInt32Register("D6", drGroup, DBG68K_REG_D6, GetReg, SetReg);
AddInt32Register("D7", drGroup, DBG68K_REG_D7, GetReg, SetReg);
// Address registers
AddInt32Register("A0", arGroup, DBG68K_REG_A0, GetReg, SetReg);
AddInt32Register("A1", arGroup, DBG68K_REG_A1, GetReg, SetReg);
AddInt32Register("A2", arGroup, DBG68K_REG_A2, GetReg, SetReg);
AddInt32Register("A3", arGroup, DBG68K_REG_A3, GetReg, SetReg);
AddInt32Register("A4", arGroup, DBG68K_REG_A4, GetReg, SetReg);
AddInt32Register("A5", arGroup, DBG68K_REG_A5, GetReg, SetReg);
AddInt32Register("A6", arGroup, DBG68K_REG_A6, GetReg, SetReg);
AddInt32Register("A7", arGroup, DBG68K_REG_A7, GetReg, SetReg);
}
CMusashi68KDebug::~CMusashi68KDebug()
{
DetachFromCPU();
}
UINT32 CMusashi68KDebug::GetSP()
{
return M68KGetRegister(m_ctx, DBG68K_REG_SP);
}
void CMusashi68KDebug::AttachToCPU()
{
if (m_ctx->Debug != NULL)
DetachFromCPU();
m_ctx->Debug = this;
m_bus = m_ctx->Bus;
m_ctx->Bus = this;
// Reset address is held at 0x000004
m_resetAddr = m_bus->Read32(0x000004);
}
void CMusashi68KDebug::DetachFromCPU()
{
if (m_ctx->Debug == NULL)
return;
m_ctx->Bus = m_bus;
m_bus = NULL;
m_ctx->Debug = NULL;
}
UINT32 CMusashi68KDebug::GetResetAddr()
{
return m_resetAddr;
}
bool CMusashi68KDebug::UpdatePC(UINT32 newPC)
{
// TODO
return false;
}
bool CMusashi68KDebug::ForceException(CException *ex)
{
// TODO
return false;
}
bool CMusashi68KDebug::ForceInterrupt(CInterrupt *in)
{
if (in->code > 6)
return false;
M68KSetIRQ(in->code + 1);
return true;
}
UINT64 CMusashi68KDebug::ReadMem(UINT32 addr, unsigned dataSize)
{
switch (dataSize)
{
case 1: return (UINT64)m_bus->Read8(addr);
case 2: return (UINT64)m_bus->Read16(addr);
case 4: return (UINT64)m_bus->Read32(addr);
default: return CCPUDebug::ReadMem(addr, dataSize);
}
}
bool CMusashi68KDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data)
{
switch (dataSize)
{
case 1: m_bus->Write8(addr, (UINT8)data); return true;
case 2: m_bus->Write16(addr, (UINT16)data); return true;
case 4: m_bus->Write32(addr, (UINT32)data); return true;
case 8: return CCPUDebug::WriteMem(addr, dataSize, data);
default: return false;
}
}
// IBus methods
UINT8 CMusashi68KDebug::Read8(UINT32 addr)
{
UINT8 data = m_bus->Read8(addr);
CheckRead8(addr, data);
return data;
}
void CMusashi68KDebug::Write8(UINT32 addr, UINT8 data)
{
m_bus->Write8(addr, data);
CheckWrite8(addr, data);
}
UINT16 CMusashi68KDebug::Read16(UINT32 addr)
{
UINT16 data = m_bus->Read16(addr);
CheckRead16(addr, data);
return data;
}
void CMusashi68KDebug::Write16(UINT32 addr, UINT16 data)
{
m_bus->Write16(addr, data);
CheckWrite16(addr, data);
}
UINT32 CMusashi68KDebug::Read32(UINT32 addr)
{
UINT32 data = m_bus->Read32(addr);
CheckRead32(addr, data);
return data;
}
void CMusashi68KDebug::Write32(UINT32 addr, UINT32 data)
{
m_bus->Write32(addr, data);
CheckWrite32(addr, data);
}
}
#endif // SUPERMODEL_DEBUGGER
+118
View File
@@ -0,0 +1,118 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Musashi68KDebug.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_MUSASHI68KDEBUG_H
#define INCLUDED_MUSASHI68KDEBUG_H
#include "68KDebug.h"
#include "CPU/Bus.h"
#include "CPU/68K/68K.h"
#include "Types.h"
// TODO - can't get this namespace to work with 68K.h for some reason - strange Visual Studio errors
namespace Debugger
{
/*
* CCPUDebug implementation for the Musashi Motorola 68000 emulator.
*/
class CMusashi68KDebug : public C68KDebug, public ::IBus
{
private:
static UINT32 GetReg(CCPUDebug *cpu, unsigned id);
static bool SetReg(CCPUDebug *cpu, unsigned id, UINT32 data);
M68KCtx *m_ctx;
UINT32 m_resetAddr;
M68KCtx m_savedCtx;
::IBus *m_bus;
// Inlined methods
void SetM68KContext()
{
M68KGetContext(&m_savedCtx);
if (m_savedCtx.Debug != this)
M68KSetContext(m_ctx);
}
void UpdateM68KContext(M68KCtx *ctx)
{
m_ctx = ctx;
}
void RestoreM68KContext()
{
if (m_savedCtx.Debug != this)
M68KSetContext(&m_savedCtx);
}
protected:
UINT32 GetSP();
public:
CMusashi68KDebug(const char *name, M68KCtx *m68K);
virtual ~CMusashi68KDebug();
// CCPUDebug methods
void AttachToCPU();
void DetachFromCPU();
UINT32 GetResetAddr();
bool UpdatePC(UINT32 pc);
bool ForceException(CException *ex);
bool ForceInterrupt(CInterrupt *in);
UINT64 ReadMem(UINT32 addr, unsigned dataSize);
bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data);
// IBus methods
UINT8 Read8(UINT32 addr);
void Write8(UINT32 addr, UINT8 data);
UINT16 Read16(UINT32 addr);
void Write16(UINT32 addr, UINT16 data);
UINT32 Read32(UINT32 addr);
void Write32(UINT32 addr, UINT32 data);
};
}
#endif // INCLUDED_MUSASHI68KDEBUG_H
#endif // SUPERMODEL_DEBUGGER
+554
View File
@@ -0,0 +1,554 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* PPCDebug.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "PPCDebug.h"
#include "CPU/PowerPC/ppc.h"
#include "CPU/PowerPC/PPCDisasm.h"
#include <cctype>
#include <string>
#define M_AA 0x00000002
#define M_LK 0x00000001
#define M_BO 0x03E00000
#define M_BD 0x0000FFFC
#define M_LI 0x03FFFFFC
#define MSR_IP 0x00000040
namespace Debugger
{
static UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id)
{
switch (id)
{
case PPCSPECIAL_LR: return ::ppc_get_lr();
case PPCSPECIAL_FPSCR: return 0; // TODO
case PPCSPECIAL_MSR: return ::ppc_read_msr();
default: return 0;
}
}
static bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data)
{
switch (id)
{
case PPCSPECIAL_LR: /* TODO */ return false;
case PPCSPECIAL_FPSCR: /* TODO */ return false;
default: return false;
}
}
static UINT8 GetCR(CCPUDebug *cpu, unsigned id)
{
return ::ppc_get_cr(id);
}
static bool SetCR(CCPUDebug *cpu, unsigned id, UINT8 data)
{
::ppc_set_cr(id, data);
return true;
}
static UINT32 GetSPR(CCPUDebug *cpu, unsigned id)
{
return ::ppc_read_spr(id);
}
static bool SetSPR(CCPUDebug *cpu, unsigned id, UINT32 data)
{
::ppc_write_spr(id, data);
return true;
}
static UINT32 GetGPR(CCPUDebug *cpu, unsigned id)
{
return ::ppc_get_gpr(id);
}
static bool SetGPR(CCPUDebug *cpu, unsigned id, UINT32 data)
{
::ppc_set_gpr(id, data);
return true;
}
static double GetFPR(CCPUDebug *cpu, unsigned id)
{
return ::ppc_get_fpr(id);
}
static bool SetFPR(CCPUDebug *cpu, unsigned id, double data)
{
::ppc_set_fpr(id, data);
return true;
}
static const char *srGroup = "Special Registers";
static const char *crGroup = "Condition Registers";
static const char *grGroup = "GPR Registers";
static const char *frGroup = "FPR Registers";
CPPCDebug::CPPCDebug(const char *name) : CCPUDebug("PPC", name, 4, 4, true, 32, 7), m_irqState(0)
{
// PC & Link registers
AddPCRegister ("pc", srGroup);
AddAddrRegister("lr", srGroup, PPCSPECIAL_LR, GetSpecialReg, SetSpecialReg);
// SPR registers
AddInt32Register ("ctr", srGroup, SPR_LR, GetSPR, SetSPR);
AddInt32Register ("xer", srGroup, SPR_XER, GetSPR, SetSPR);
//AddStatus32Register("xer", srGroup, SPR_XER, "SOC", GetSPR, SetSPR); //TODO: bit mapping is wrong
AddInt32Register ("srr0", srGroup, SPR_SRR0, GetSPR, SetSPR);
AddInt32Register ("srr1", srGroup, SPR_SRR1, GetSPR, SetSPR);
AddInt32Register ("msr", srGroup, PPCSPECIAL_MSR, GetSpecialReg, SetSpecialReg);
AddInt32Register ("sdr1", srGroup, SPR603E_SDR1, GetSPR, SetSPR);
AddInt32Register ("imiss",srGroup, SPR603E_IMISS, GetSPR, SetSPR);
AddInt32Register ("dmiss",srGroup, SPR603E_DMISS, GetSPR, SetSPR);
AddInt32Register ("hid0", srGroup, SPR603E_HID0, GetSPR, SetSPR);
AddInt32Register ("hid1", srGroup, SPR603E_HID1, GetSPR, SetSPR);
// etc...
// Condition registers
for (unsigned id = 0; id < 8; id++)
{
sprintf(m_crNames[id], "cr%u", id);
AddStatus8Register(m_crNames[id], crGroup, id, "<>=O", GetCR, SetCR);
}
//AddStatus16Register("fpscr", "Condition Registers", PPCSPECIAL_FPSCR, "FEVOUZX789ABCRI 0123", GetSpecial, SetSpecial);
// GPR registers
for (unsigned id = 0; id < 32; id++)
{
sprintf(m_gprNames[id], "r%u", id);
AddInt32Register(m_gprNames[id], grGroup, id, GetGPR, SetGPR);
}
// FPR registers
for (unsigned id = 0; id < 32; id++)
{
sprintf(m_fprNames[id], "f%u", id);
AddFPointRegister(m_fprNames[id], frGroup, id, GetFPR, SetFPR);
}
// Exceptions
AddException("IRQ", EXCEPTION_IRQ, "External Interrupt");
AddException("DEC", EXCEPTION_DECREMENTER, "Decrement Overflow");
AddException("TRAP", EXCEPTION_TRAP, "Program Exception/Trap");
AddException("SYSCALL", EXCEPTION_SYSTEM_CALL, "System Call");
AddException("SMI", EXCEPTION_SMI, "SMI");
AddException("DSI", EXCEPTION_DSI, "DSI");
AddException("ISI", EXCEPTION_ISI, "ISI");
}
CPPCDebug::~CPPCDebug()
{
DetachFromCPU();
}
void CPPCDebug::AttachToCPU()
{
::ppc_attach_debugger(this);
}
::IBus *CPPCDebug::AttachBus(::IBus *bus)
{
m_bus = bus;
return this;
}
void CPPCDebug::DetachFromCPU()
{
::ppc_detach_debugger();
}
::IBus *CPPCDebug::DetachBus()
{
::IBus *bus = m_bus;
m_bus = NULL;
return bus;
}
UINT32 CPPCDebug::GetResetAddr()
{
// Reset address appears to be hardcoded to 0xFFF00100
return 0xFFF00100;
}
bool CPPCDebug::UpdatePC(UINT32 pc)
{
::ppc_set_pc(pc);
return true;
}
bool CPPCDebug::ForceException(CException *ex)
{
// TODO - no way to force exceptions
return false;
}
bool CPPCDebug::ForceInterrupt(CInterrupt *in)
{
if (in->code > 7)
return false;
UINT8 irqState = m_bus->Read8(0xF0100018) | 1<<in->code;
m_bus->Write8(0xF0100018, irqState);
::ppc_set_irq_line(1); // TODO - what is irqline arg for? not actually used?
return true;
}
UINT64 CPPCDebug::ReadMem(UINT32 addr, unsigned dataSize)
{
switch (dataSize)
{
case 1: return (UINT64)m_bus->Read8(addr);
case 2: return (UINT64)m_bus->Read16(addr);
case 4: return (UINT64)m_bus->Read32(addr);
case 8: return m_bus->Read64(addr);
default: return 0;
}
}
bool CPPCDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data)
{
switch (dataSize)
{
case 1: m_bus->Write8(addr, (UINT8)data); return true;
case 2: m_bus->Write16(addr, (UINT16)data); return true;
case 4: m_bus->Write32(addr, (UINT32)data); return true;
case 8: m_bus->Write64(addr, data); return true;
default: return false;
}
}
void CPPCDebug::CheckException(UINT16 exCode)
{
CCPUDebug::CPUException(exCode);
if (exCode == EXCEPTION_IRQ)
{
UINT8 irqState = m_bus->Read8(0xF0100018); // TODO - replace this with function pointer
UINT8 newIRQs = (irqState^m_irqState)&irqState;
for (int intCode = 0; newIRQs && intCode < 8; intCode++)
{
if (newIRQs&0x01)
CPUInterrupt(intCode);
newIRQs >>= 1;
}
m_irqState = irqState;
}
}
int CPPCDebug::Disassemble(UINT32 addr, char *mnemonic, char *operands)
{
char opStr[255];
char valStr[40];
UINT32 opcode = m_bus->Read32(addr);
operands[0] = '\0';
if (!::DisassemblePowerPC(opcode, addr, mnemonic, opStr, true))
{
char *o = opStr;
char *s = strstr(o, "0x");
while (s)
{
strncpy(operands, o, s - o);
operands[s - o] = '\0';
s += 2;
char *p = s;
unsigned len = 0;
UINT64 data = 0;
while (p)
{
char c = toupper(*(p++));
if (c >= '0' && c <= '9')
{
data <<= 4;
data += (UINT64)(c - '0');
}
else if (c >= 'A' && c <= 'F')
{
data <<= 4;
data += (UINT64)(10 + c - 'A');
}
else
break;
len++;
}
unsigned dataSize = (p - s) / 2;
if (dataSize == (unsigned)(memBusWidth / 8))
{
EOpFlags opFlags = GetOpFlags(addr, opcode);
FormatJumpAddress(valStr, (UINT32)data, opFlags);
}
else
FormatData(valStr, dataSize, data);
strcat(operands, valStr);
operands += strlen(operands);
o = p - 1;
s = strstr(o, "0x");
}
strcat(operands, o);
return 4;
}
else
return -4;
}
EOpFlags CPPCDebug::GetOpFlags(UINT32 addr, UINT32 opcode)
{
EOpFlags opFlags;
UINT32 op = opcode>>26;
if (op == 0x10)
{
// Instruction is branch conditional: bc, bca, bcl or bcla
UINT32 bo = (opcode&M_BO)>>21;
if (opcode&M_LK)
{
if (opcode&M_AA)
opFlags = JumpSub; // bcla
else if (bo&0x04)
opFlags = (EOpFlags)(JumpSub | Relative); // bcl without counter
else
opFlags = (EOpFlags)(JumpSub | Relative); // bcl with counter
}
else
{
if (opcode&M_AA)
opFlags = JumpSimple; // bca
else if (bo&0x04)
opFlags = (EOpFlags)(JumpSimple | Relative); // bc without counter
else
opFlags = (EOpFlags)(JumpLoop | Relative); // bc with counter
}
// Check BO is not just branch always
return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional));
}
else if (op == 0x12)
{
// Instruction is branch: b, ba, bl or bla
if (opcode&M_LK)
{
if (opcode&M_AA)
return JumpSub; // bla
else
return (EOpFlags)(JumpSub | Relative); // bl
}
else
{
if (opcode&M_AA)
return JumpSimple; // ba
else
return (EOpFlags)(JumpSimple | Relative); // b
}
}
else if (op == 0x13)
{
UINT32 exOp = (opcode>>1)&0x3ff;
UINT32 bo = (opcode&M_BO)>>21;
if (exOp == 0x0210)
{
// Instruction is branch conditional to count register: bcctr or bcctrl
if (opcode&M_LK)
opFlags = (EOpFlags)(JumpSub | Relative); // bcctrl
else if (bo&0x04)
opFlags = (EOpFlags)(JumpSimple | Relative); // bcctr without counter
else
opFlags = (EOpFlags)(JumpLoop | Relative); // bcctr with counter
// Check BO is not just branch always
return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional));
}
else if (exOp == 0x0010)
{
// Instruction is branch conditional to link register: bclr or bclrl
if (opcode&M_LK)
opFlags = (EOpFlags)(JumpSub | ReturnSub); // bclrl
else
opFlags = ReturnSub; // bclr
// Check BO is not just branch always
return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional));
}
else if (exOp == 0x0032)
{
// Instruction is return from interrupt: rfi
return ReturnEx;
}
// TODO - traps etc
}
return NormalOp;
}
bool CPPCDebug::GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr)
{
// Check instruction is one of following branches: b, ba, bl, bla, bc, bca, bcl or bcla
UINT32 disp;
UINT32 op = opcode>>26;
if (op == 0x10)
{
// Instruction is b, ba, bl or bla, so calculate branch displacement
disp = ((opcode&M_BD)>>2) * 4;
if (disp & 0x00008000)
disp |= 0xFFFF0000; // Sign extended
if (opcode&M_AA)
jumpAddr = disp; // ba or bla
else
jumpAddr = addr + disp; // b or bl
return true;
}
else if (op == 0x12)
{
// Instruction is bc, bca, bcl or bcla, so calculate branch displacement
disp = ((opcode&M_LI) >> 2) * 4;
if (disp & 0x02000000)
disp |= 0xFC000000; // Sign extended
if (opcode&M_AA)
jumpAddr = disp; // bca or bcla
else
jumpAddr = addr + disp; // bc or bcl
return true;
}
return false;
}
bool CPPCDebug::GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr)
{
UINT32 op = opcode>>26;
if ((op == 0x10 || op == 0x12) && (opcode&M_LK))
{
// Instruction is bl, bla, bcl or bcla (TODO - add bclrl?)
retAddr = addr + 4;
return true;
}
return false;
}
bool CPPCDebug::GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr)
{
// Check instruction is one of following: bclr, bclrl or rfi
if ((opcode>>26) != 0x13)
return false;
UINT32 exOp = (opcode>>1)&0x3ff;
if (exOp == 0x0010)
{
// For bclr and blclr, return address is in link register
retAddr = ::ppc_get_lr();
return true;
}
else if (exOp == 0x0032)
{
// For rfi, return address is in SRR0
retAddr = ::ppc_read_spr(SPR_SRR0);
return true;
}
return false;
}
bool CPPCDebug::GetHandlerAddr(CException *ex, UINT32 &handlerAddr)
{
UINT32 msr = ::ppc_read_msr();
UINT32 base = (msr&MSR_IP ? 0xFFF00000 : 0x00000000);
switch (ex->code)
{
case EXCEPTION_DSI: handlerAddr = base + 0x0300; return true;
case EXCEPTION_ISI: handlerAddr = base + 0x0400; return true;
case EXCEPTION_IRQ: handlerAddr = base + 0x0500; return true;
case EXCEPTION_TRAP: handlerAddr = base + 0x0700; return true;
case EXCEPTION_DECREMENTER: handlerAddr = base + 0x0900; return true;
case EXCEPTION_SYSTEM_CALL: handlerAddr = base + 0x0C00; return true;
case EXCEPTION_SMI: handlerAddr = base + 0x1400; return true;
default: return false;
}
}
bool CPPCDebug::GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr)
{
UINT32 msr = ::ppc_read_msr();
handlerAddr = (msr&MSR_IP ? 0xFFF00500 : 0x00000500);
return true;
}
// IBus methods
UINT8 CPPCDebug::Read8(UINT32 addr)
{
UINT8 data = m_bus->Read8(addr);
CheckRead8(addr, data);
return data;
}
UINT16 CPPCDebug::Read16(UINT32 addr)
{
UINT16 data = m_bus->Read16(addr);
CheckRead16(addr, data);
return data;
}
UINT32 CPPCDebug::Read32(UINT32 addr)
{
UINT32 data = m_bus->Read32(addr);
CheckRead32(addr, data);
return data;
}
UINT64 CPPCDebug::Read64(UINT32 addr)
{
UINT64 data = m_bus->Read64(addr);
CheckRead64(addr, data);
return data;
}
void CPPCDebug::Write8(UINT32 addr, UINT8 data)
{
m_bus->Write8(addr, data);
CheckWrite8(addr, data);
}
void CPPCDebug::Write16(UINT32 addr, UINT16 data)
{
m_bus->Write16(addr, data);
CheckWrite16(addr, data);
}
void CPPCDebug::Write32(UINT32 addr, UINT32 data)
{
m_bus->Write32(addr, data);
CheckWrite32(addr, data);
}
void CPPCDebug::Write64(UINT32 addr, UINT64 data)
{
m_bus->Write64(addr, data);
CheckWrite64(addr, data);
}
}
#endif // SUPERMODEL_DEBUGGER
+118
View File
@@ -0,0 +1,118 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* PPCDebug.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_PPCDEBUG_H
#define INCLUDED_PPCDEBUG_H
#include "Debugger/CPUDebug.h"
#include "CPU/Bus.h"
#include "Types.h"
#define PPCSPECIAL_LR 0
#define PPCSPECIAL_FPSCR 1
#define PPCSPECIAL_MSR 2
namespace Debugger
{
/*
* CCPUDebug implementation for the PowerPC PPC603 emulator.
*/
class CPPCDebug : public CCPUDebug, public ::IBus
{
private:
char m_crNames[32][5];
char m_gprNames[32][4];
char m_fprNames[32][4];
::IBus *m_bus;
UINT8 m_irqState;
public:
CPPCDebug(const char *name);
virtual ~CPPCDebug();
// CCPUDebug methods
void AttachToCPU();
::IBus *AttachBus(::IBus *bus);
void DetachFromCPU();
::IBus *DetachBus();
void CheckException(UINT16 exCode);
UINT32 GetResetAddr();
bool UpdatePC(UINT32 pc);
bool ForceException(CException *ex);
bool ForceInterrupt(CInterrupt *in);
UINT64 ReadMem(UINT32 addr, unsigned dataSize);
bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data);
int Disassemble(UINT32 addr, char *mnemonic, char *operands);
EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode);
bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr);
bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr);
bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr);
bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr);
bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr);
// IBus methods
UINT8 Read8(UINT32 addr);
UINT16 Read16(UINT32 addr);
UINT32 Read32(UINT32 addr);
UINT64 Read64(UINT32 addr);
void Write8(UINT32 addr, UINT8 data);
void Write16(UINT32 addr, UINT16 data);
void Write32(UINT32 addr, UINT32 data);
void Write64(UINT32 addr, UINT64 data);
};
}
#endif // INCLUDED_PPCDEBUG_H
#endif // SUPERMODEL_DEBUGGER
+283
View File
@@ -0,0 +1,283 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Turbo68KDebug.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Turbo68KDebug.h"
#include <cctype>
#include <string>
namespace Debugger
{
UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id)
{
switch (id)
{
case TBO68K_REG_SP: return (UINT32)turbo68kcontext_68000.a[7];
case TBO68K_REG_SR: return (UINT32)turbo68kcontext_68000.sr;
default: return 0;
}
}
bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data)
{
switch (id)
{
case TBO68K_REG_SP: turbo68kcontext_68000.a[7] = data; return true;
case TBO68K_REG_SR: turbo68kcontext_68000.sr = data; return true;
default: return false;
}
}
UINT32 GetDataReg(CCPUDebug *cpu, unsigned id)
{
return (UINT32)turbo68kcontext_68000.d[id];
}
bool SetDataReg(CCPUDebug *cpu, unsigned id, UINT32 data)
{
turbo68kcontext_68000.d[id] = data;
return true;
}
UINT32 GetAddressReg(CCPUDebug *cpu, unsigned id)
{
return (UINT32)turbo68kcontext_68000.a[id];
}
bool SetAddressReg(CCPUDebug *cpu, unsigned id, UINT32 data)
{
turbo68kcontext_68000.a[id] = data;
return true;
}
static const char *srGroup = "Special Registers";
static const char *drGroup = "Data Registers";
static const char *arGroup = "Address Regsters";
CTurbo68KDebug::CTurbo68KDebug(const char *name) : C68KDebug(name), m_resetAddr(0)
{
// Special registers
AddPCRegister ("PC", srGroup);
AddAddrRegister ("SP", srGroup, TBO68K_REG_SP, GetSpecialReg, SetSpecialReg);
AddStatus32Register("SR", srGroup, TBO68K_REG_SR, "TtSM.210...XNZVC", GetSpecialReg, SetSpecialReg);
// Data registers
for (unsigned id = 0; id < 8; id++)
{
sprintf(m_drNames[id], "D%u", id);
AddInt32Register(m_drNames[id], drGroup, id, GetDataReg, SetDataReg);
}
// Address registers
for (unsigned id = 0; id < 8; id++)
{
sprintf(m_arNames[id], "A%u", id);
AddInt32Register(m_arNames[id], arGroup, id, GetAddressReg, SetAddressReg);
}
}
CTurbo68KDebug::~CTurbo68KDebug()
{
DetachFromCPU();
}
bool __cdecl DebugHandler(TURBO68K_INT32 pc, TURBO68K_INT32 opcode)
{
// Return true to let Turbo68K know if PC was changed by user
return debug->CPUExecute((UINT32)pc, (UINT32)opcode, 1); // TODO - lastCycles
}
void __cdecl InterruptHandler(TURBO68K_UINT32 intVec)
{
// TODO - is following correct handling of interrupts? - need to check readme file
if (intVec > 47)
return;
else if (intVec >= 25 || intVec < 32)
{
debug->CPUException(25);
debug->CPUInterrupt((UINT16)intVec - 25);
}
else
debug->CPUException((UINT16)intVec);
if (origIntAckPtr != NULL)
origIntAckPtr(intVec);
}
void CTurbo68KDebug::AttachToCPU()
{
if (debug != NULL)
DetachFromCPU();
debug = this;
origDebugPtr = (DebugPtr)turbo68kcontext_68000.Debug;
origIntAckPtr = (IntAckPtr)turbo68kcontext_68000.InterruptAcknowledge;
origRead8Regions = (TURBO68K_DATAREGION*)Turbo68KGetReadByte(TURBO68K_NULL);
origRead16Regions = (TURBO68K_DATAREGION*)Turbo68KGetReadWord(TURBO68K_NULL);
origRead32Regions = (TURBO68K_DATAREGION*)Turbo68KGetReadLong(TURBO68K_NULL);
origWrite8Regions = (TURBO68K_DATAREGION*)Turbo68KGetWriteByte(TURBO68K_NULL);
origWrite16Regions = (TURBO68K_DATAREGION*)Turbo68KGetWriteWord(TURBO68K_NULL);
origWrite32Regions = (TURBO68K_DATAREGION*)Turbo68KGetWriteLong(TURBO68K_NULL);
turbo68kcontext_68000.Debug = DebugHandler;
turbo68kcontext_68000.InterruptAcknowledge = InterruptHandler;
debugRead8Regions[0].handler = ReadByteDebug;
debugRead16Regions[0].handler = ReadWordDebug;
debugRead32Regions[0].handler = ReadLongDebug;
debugWrite8Regions[0].handler = WriteByteDebug;
debugWrite16Regions[0].handler = WriteWordDebug;
debugWrite32Regions[0].handler = WriteLongDebug;
Turbo68KSetReadByte(debugRead8Regions, TURBO68K_NULL);
Turbo68KSetReadWord(debugRead16Regions, TURBO68K_NULL);
Turbo68KSetReadLong(debugRead32Regions, TURBO68K_NULL);
Turbo68KSetWriteByte(debugWrite8Regions, TURBO68K_NULL);
Turbo68KSetWriteWord(debugWrite16Regions, TURBO68K_NULL);
Turbo68KSetWriteLong(debugWrite32Regions, TURBO68K_NULL);
// Reset address is held at 0x000004
m_resetAddr = ReadLongDirect(0x000004);
}
void CTurbo68KDebug::DetachFromCPU()
{
if (debug == NULL)
return;
turbo68kcontext_68000.Debug = origDebugPtr;
turbo68kcontext_68000.InterruptAcknowledge = origIntAckPtr;
Turbo68KSetReadByte(origRead8Regions, TURBO68K_NULL);
Turbo68KSetReadWord(origRead16Regions, TURBO68K_NULL);
Turbo68KSetReadLong(origRead32Regions, TURBO68K_NULL);
Turbo68KSetWriteByte(origWrite8Regions, TURBO68K_NULL);
Turbo68KSetWriteWord(origWrite16Regions, TURBO68K_NULL);
Turbo68KSetWriteLong(origWrite32Regions, TURBO68K_NULL);
debug = NULL;
}
UINT32 CTurbo68KDebug::GetResetAddr()
{
return m_resetAddr;
}
UINT32 CTurbo68KDebug::GetSP()
{
return (UINT32)turbo68kcontext_68000.a[7];
}
bool CTurbo68KDebug::UpdatePC(UINT32 newPC)
{
turbo68kcontext_68000.pc = newPC;
return true;
}
bool CTurbo68KDebug::ForceException(CException *ex)
{
//switch (ex->code)
//{
// TODO
//}
return false;
}
bool CTurbo68KDebug::ForceInterrupt(CInterrupt *in)
{
if (in->code > 6)
return false;
//Turbo68KInterrupt(in->code, TURBO64K_AUTOVECTOR);
//Turbo68KProcessInterrupts(); TODO - call this?
return false; // TODO
}
UINT64 CTurbo68KDebug::ReadMem(UINT32 addr, unsigned dataSize)
{
switch (dataSize)
{
case 1: return ReadByteDirect(addr);
case 2: return ReadWordDirect(addr);
case 4: return ReadLongDirect(addr);
default: return CCPUDebug::ReadMem(addr, dataSize);
}
}
bool CTurbo68KDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data)
{
switch (dataSize)
{
case 1: WriteByteDirect(addr, (TURBO68K_UINT8)data); return true;
case 2: WriteWordDirect(addr, (TURBO68K_UINT16)data); return true;
case 4: WriteLongDirect(addr, (TURBO68K_UINT32)data); return true;
default: return CCPUDebug::WriteMem(addr, dataSize, data);
}
}
TURBO68K_UINT8 __cdecl ReadByteDebug(TURBO68K_UINT32 addr)
{
TURBO68K_UINT8 data = ReadByteDirect(addr);
debug->CheckRead8((UINT32)addr, (UINT8)data);
return data;
}
TURBO68K_UINT16 __cdecl ReadWordDebug(TURBO68K_UINT32 addr)
{
TURBO68K_UINT16 data = ReadWordDirect(addr);
debug->CheckRead16((UINT32)addr, (UINT16)data);
return data;
}
TURBO68K_UINT32 __cdecl ReadLongDebug(TURBO68K_UINT32 addr)
{
TURBO68K_UINT32 data = ReadLongDirect(addr);
debug->CheckRead32((UINT32)addr, (UINT32)data);
return data;
}
void __cdecl WriteByteDebug(TURBO68K_UINT32 addr, TURBO68K_UINT8 data)
{
WriteByteDirect(addr, data);
debug->CheckWrite8((UINT32)addr, (UINT8)data);
}
void __cdecl WriteWordDebug(TURBO68K_UINT32 addr, TURBO68K_UINT16 data)
{
WriteWordDirect(addr, data);
debug->CheckWrite16((UINT32)addr, (UINT16)data);
}
void __cdecl WriteLongDebug(TURBO68K_UINT32 addr, TURBO68K_UINT32 data)
{
WriteLongDirect(addr, data);
debug->CheckWrite32((UINT32)addr, (UINT32)data);
}
}
#endif // SUPERMODEL_DEBUGGER
+341
View File
@@ -0,0 +1,341 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Turbo68KDebug.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_TURBO68KDEBUG_H
#define INCLUDED_TURBO68KDEBUG_H
#include "Debugger/CPU/68KDebug.h"
#include "Types.h"
#include "CPU/68K/Turbo68K/Turbo68K.h"
#define TBO68K_REG_SP 0
#define TBO68K_REG_SR 1
#define USE_NATIVE_READ 0
#define USE_NATIVE_WRITE 0
namespace Debugger
{
class CTurbo68KDebug;
static CTurbo68KDebug *debug = NULL;
typedef bool (*DebugPtr)(TURBO68K_INT32 pc, TURBO68K_INT32 opcode);
typedef void (*IntAckPtr)(TURBO68K_UINT32 intVec);
static DebugPtr origDebugPtr = NULL;
static IntAckPtr origIntAckPtr = NULL;
static TURBO68K_DATAREGION *origRead8Regions;
static TURBO68K_DATAREGION *origRead16Regions;
static TURBO68K_DATAREGION *origRead32Regions;
static TURBO68K_DATAREGION *origWrite8Regions;
static TURBO68K_DATAREGION *origWrite16Regions;
static TURBO68K_DATAREGION *origWrite32Regions;
static TURBO68K_DATAREGION debugRead8Regions[] =
{
{ 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL },
{ -1, -1, TURBO68K_NULL, TURBO68K_NULL }
};
static TURBO68K_DATAREGION debugRead16Regions[] =
{
{ 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL },
{ -1, -1, TURBO68K_NULL, TURBO68K_NULL }
};
static TURBO68K_DATAREGION debugRead32Regions[] =
{
{ 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL },
{ -1, -1, TURBO68K_NULL, TURBO68K_NULL }
};
static TURBO68K_DATAREGION debugWrite8Regions[] =
{
{ 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL },
{ -1, -1, TURBO68K_NULL, TURBO68K_NULL }
};
static TURBO68K_DATAREGION debugWrite16Regions[] =
{
{ 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL },
{ -1, -1, TURBO68K_NULL, TURBO68K_NULL }
};
static TURBO68K_DATAREGION debugWrite32Regions[] =
{
{ 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL },
{ -1, -1, TURBO68K_NULL, TURBO68K_NULL }
};
static UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id);
static bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data);
static UINT32 GetDataReg(CCPUDebug *cpu, unsigned id);
static bool SetDataReg(CCPUDebug *cpu, unsigned id, UINT32 data) ;
static UINT32 GetAddressReg(CCPUDebug *cpu, unsigned id);
static bool SetAddressReg(CCPUDebug *cpu, unsigned id, UINT32 data);
static TURBO68K_UINT8 ReadByteDebug(TURBO68K_UINT32 addr);
static TURBO68K_UINT16 ReadWordDebug(TURBO68K_UINT32 addr);
static TURBO68K_UINT32 ReadLongDebug(TURBO68K_UINT32 addr);
static void WriteByteDebug(TURBO68K_UINT32 addr, TURBO68K_UINT8 data);
static void WriteWordDebug(TURBO68K_UINT32 addr, TURBO68K_UINT16 data);
static void WriteLongDebug(TURBO68K_UINT32 addr, TURBO68K_UINT32 data);
static TURBO68K_UINT8 ReadByteDirect(TURBO68K_UINT32 addr);
static TURBO68K_UINT16 ReadWordDirect(TURBO68K_UINT32 addr);
static TURBO68K_UINT32 ReadLongDirect(TURBO68K_UINT32 addr);
static void WriteByteDirect(TURBO68K_UINT32 addr, TURBO68K_UINT8 data);
static void WriteWordDirect(TURBO68K_UINT32 addr, TURBO68K_UINT16 data);
static void WriteLongDirect(TURBO68K_UINT32 addr, TURBO68K_UINT32 data);
/*
* CCPUDebug implementation for the Turbo68K Motorola 68000 emulator.
*/
class CTurbo68KDebug : public C68KDebug
{
private:
char m_drNames[8][3];
char m_arNames[8][3];
UINT32 m_resetAddr;
protected:
UINT32 GetSP();
public:
CTurbo68KDebug(const char *name);
virtual ~CTurbo68KDebug();
// CCPUDebug methods
void AttachToCPU();
void DetachFromCPU();
UINT32 GetResetAddr();
bool UpdatePC(UINT32 pc);
bool ForceException(CException *ex);
bool ForceInterrupt(CInterrupt *in);
UINT64 ReadMem(UINT32 addr, unsigned dataSize);
bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data);
};
//
// Inlined functions
//
typedef TURBO68K_UINT8 (*ReadByteFPtr)(TURBO68K_UINT32);
typedef TURBO68K_UINT16 (*ReadWordFPtr)(TURBO68K_UINT32);
typedef TURBO68K_UINT32 (*ReadLongFPtr)(TURBO68K_UINT32);
typedef void (*WriteByteFPtr)(TURBO68K_UINT32, TURBO68K_UINT8);
typedef void (*WriteWordFPtr)(TURBO68K_UINT32, TURBO68K_UINT16);
typedef void (*WriteLongFPtr)(TURBO68K_UINT32, TURBO68K_UINT32);
inline TURBO68K_UINT8 ReadByteDirect(TURBO68K_UINT32 addr)
{
#if USE_NATIVE_READ
Turbo68KSetReadByte(origRead8Regions, TURBO68K_NULL);
TURBO68K_UINT8 data = Turbo68KReadByte(addr);
Turbo68KSetReadByte(debugRead8Regions, TURBO68K_NULL);
return data;
#else
for (TURBO68K_DATAREGION *region = origRead8Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++)
{
if (region->base <= addr && addr <= region->limit)
{
if (region->ptr != TURBO68K_NULL)
{
// Turbo68K requires native memory to be byte swapped, so must reverse this when reading bytes
TURBO68K_UINT8 *dataP = (TURBO68K_UINT8*)(region->ptr + (addr^1));
return *dataP;
}
else
{
ReadByteFPtr fPtr = (ReadByteFPtr)region->handler;
return fPtr(addr);
}
}
}
return 0;
#endif
}
inline TURBO68K_UINT16 ReadWordDirect(TURBO68K_UINT32 addr)
{
#if USE_NATIVE_READ
Turbo68KSetReadWord(origRead16Regions, TURBO68K_NULL);
TURBO68K_UINT16 data = Turbo68KReadWord(addr);
Turbo68KSetReadWord(debugRead16Regions, TURBO68K_NULL);
return data;
#else
for (TURBO68K_DATAREGION *region = origRead16Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++)
{
if (region->base <= addr && addr <= region->limit)
{
if (region->ptr != TURBO68K_NULL)
{
// Turbo68K requires native memory to be byte swapped, so must reverse this when reading bytes
TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr);
return *dataP;
}
else
{
ReadWordFPtr fPtr = (ReadWordFPtr)region->handler;
return fPtr(addr);
}
}
}
return 0;
#endif
}
inline TURBO68K_UINT32 ReadLongDirect(TURBO68K_UINT32 addr)
{
#if USE_NATIVE_READ
Turbo68KSetReadLong(origRead32Regions, TURBO68K_NULL);
TURBO68K_UINT32 data = Turbo68KReadLong(addr);
Turbo68KSetReadLong(debugRead32Regions, TURBO68K_NULL);
return data;
#else
for (TURBO68K_DATAREGION *region = origRead32Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++)
{
if (region->base <= addr && addr <= region->limit)
{
if (region->ptr != TURBO68K_NULL)
{
// Turbo68K requires native memory to be byte swapped, so must reverse this when reading bytes
TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr);
return (TURBO68K_UINT32)dataP[1] | ((TURBO68K_UINT32)dataP[0])<<16;
}
else
{
ReadLongFPtr fPtr = (ReadLongFPtr)region->handler;
return fPtr(addr);
}
}
}
return 0;
#endif
}
inline void WriteByteDirect(TURBO68K_UINT32 addr, TURBO68K_UINT8 data)
{
#if USE_NATIVE_WRITE
Turbo68KSetWriteByte(origWrite8Regions, TURBO68K_NULL);
Turbo68KWriteByte(addr, data);
Turbo68KSetWriteByte(debugWrite8Regions, TURBO68K_NULL);
#else
for (TURBO68K_DATAREGION *region = origWrite8Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++)
{
if (region->base <= addr && addr <= region->limit)
{
if (region->ptr != TURBO68K_NULL)
{
// Turbo68K requires native memory to be byte swapped, so must reverse this when writing bytes
TURBO68K_UINT8 *dataP = (TURBO68K_UINT8*)(region->ptr + (addr^1));
*dataP = data;
return;
}
else
{
WriteByteFPtr fPtr = (WriteByteFPtr)region->handler;
fPtr(addr, data);
return;
}
}
}
return;
#endif
}
inline void WriteWordDirect(TURBO68K_UINT32 addr, TURBO68K_UINT16 data)
{
#if USE_NATIVE_WRITE
Turbo68KSetWriteWord(origWrite16Regions, TURBO68K_NULL);
Turbo68KWriteWord(addr, data);
Turbo68KSetWriteWord(debugWrite16Regions, TURBO68K_NULL);
#else
for (TURBO68K_DATAREGION *region = origWrite16Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++)
{
if (region->base <= addr && addr <= region->limit)
{
if (region->ptr != TURBO68K_NULL)
{
// Turbo68K requires native memory to be byte swapped, so must reverse this when writing bytes
TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr);
*dataP = data;
return;
}
else
{
WriteWordFPtr fPtr = (WriteWordFPtr)region->handler;
fPtr(addr, data);
return;
}
}
}
return;
#endif
}
inline void WriteLongDirect(TURBO68K_UINT32 addr, TURBO68K_UINT32 data)
{
#if USE_NATIVE_WRITE
Turbo68KSetWriteLong(origWrite32Regions, TURBO68K_NULL);
Turbo68KWriteLong(addr, data);
Turbo68KSetWriteLong(debugWrite32Regions, TURBO68K_NULL);
#else
for (TURBO68K_DATAREGION *region = origWrite32Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++)
{
if (region->base <= addr && addr <= region->limit)
{
if (region->ptr != TURBO68K_NULL)
{
// Turbo68K requires native memory to be byte swapped, so must reverse this when writing bytes
TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr);
dataP[0] = data>>16;
dataP[1] = data&0xFFFF;
return;
}
else
{
WriteLongFPtr fPtr = (WriteLongFPtr)region->handler;
fPtr(addr, data);
return;
}
}
}
return;
#endif
}
}
#endif // INCLUDED_TURBO68KDEBUG_H
#endif // SUPERMODEL_DEBUGGER
+727
View File
@@ -0,0 +1,727 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Z80Debug.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Z80Debug.h"
#include "CPU/Z80/Z80.h"
#include <string>
namespace Debugger
{
// Instruction templates
static char *templates[5][256] = {
{
// Table 0: single byte instructions
"NOP","LD BC,@a","LD (BC),A","INC BC","INC B","DEC B","LD B,@d","RLCA",
"EX AF,AF'","ADD HL,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,@d","RRCA",
"DJNZ @b","LD DE,@a","LD (DE),A","INC DE","INC D","DEC D","LD D,@d","RLA",
"JR @b","ADD HL,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,@d","RRA",
"JR NZ,@b","LD HL,@a","LD (@a),HL","INC HL","INC H","DEC H","LD H,@d","DAA",
"JR Z,@b","ADD HL,HL","LD HL,(@a)","DEC HL","INC L","DEC L","LD L,@d","CPL",
"JR NC,@b","LD SP,@a","LD (@a),A","INC SP","INC (HL)","DEC (HL)","LD (HL),@d","SCF",
"JR C,@b","ADD HL,SP","LD A,(@a)","DEC SP","INC A","DEC A","LD A,@d","CCF",
"LD B,B","LD B,C","LD B,D","LD B,E","LD B,H","LD B,L","LD B,(HL)","LD B,A",
"LD C,B","LD C,C","LD C,D","LD C,E","LD C,H","LD C,L","LD C,(HL)","LD C,A",
"LD D,B","LD D,C","LD D,D","LD D,E","LD D,H","LD D,L","LD D,(HL)","LD D,A",
"LD E,B","LD E,C","LD E,D","LD E,E","LD E,H","LD E,L","LD E,(HL)","LD E,A",
"LD H,B","LD H,C","LD H,D","LD H,E","LD H,H","LD H,L","LD H,(HL)","LD H,A",
"LD L,B","LD L,C","LD L,D","LD L,E","LD L,H","LD L,L","LD L,(HL)","LD L,A",
"LD (HL),B","LD (HL),C","LD (HL),D","LD (HL),E","LD (HL),H","LD (HL),L","HALT","LD (HL),A",
"LD A,B","LD A,C","LD A,D","LD A,E","LD A,H","LD A,L","LD A,(HL)","LD A,A",
"ADD B","ADD C","ADD D","ADD E","ADD H","ADD L","ADD (HL)","ADD A",
"ADC B","ADC C","ADC D","ADC E","ADC H","ADC L","ADC (HL)","ADC A",
"SUB B","SUB C","SUB D","SUB E","SUB H","SUB L","SUB (HL)","SUB A",
"SBC B","SBC C","SBC D","SBC E","SBC H","SBC L","SBC (HL)","SBC A",
"AND B","AND C","AND D","AND E","AND H","AND L","AND (HL)","AND A",
"XOR B","XOR C","XOR D","XOR E","XOR H","XOR L","XOR (HL)","XOR A",
"OR B","OR C","OR D","OR E","OR H","OR L","OR (HL)","OR A",
"CP B","CP C","CP D","CP E","CP H","CP L","CP (HL)","CP A",
"RET NZ","POP BC","JP NZ,@j","JP @j","CALL NZ,@j","PUSH BC","ADD @d","RST 00h",
"RET Z","RET","JP Z,@j","PFX_CB","CALL Z,@j","CALL @j","ADC @d","RST 08h",
"RET NC","POP DE","JP NC,@j","OUTA (@p)","CALL NC,@j","PUSH DE","SUB @d","RST 10h",
"RET C","EXX","JP C,@j","INA (@p)","CALL C,@j","PFX_DD","SBC @d","RST 18h",
"RET PO","POP HL","JP PO,@j","EX HL,(SP)","CALL PO,@j","PUSH HL","AND @d","RST 20h",
"RET PE","LD PC,HL","JP PE,@j","EX DE,HL","CALL PE,@j","PFX_ED","XOR @d","RST 28h",
"RET P","POP AF","JP P,@j","DI","CALL P,@j","PUSH AF","OR @d","RST 30h",
"RET M","LD SP,HL","JP M,@j","EI","CALL M,@j","PFX_FD","CP @d","RST 38h"
}, {
// Table 1: two byte instructions of form CB-XX
"RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (HL)","RLC A",
"RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (HL)","RRC A",
"RL B","RL C","RL D","RL E","RL H","RL L","RL (HL)","RL A",
"RR B","RR C","RR D","RR E","RR H","RR L","RR (HL)","RR A",
"SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (HL)","SLA A",
"SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (HL)","SRA A",
"SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (HL)","SLL A",
"SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (HL)","SRL A",
"BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(HL)","BIT 0,A",
"BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(HL)","BIT 1,A",
"BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(HL)","BIT 2,A",
"BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(HL)","BIT 3,A",
"BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(HL)","BIT 4,A",
"BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(HL)","BIT 5,A",
"BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(HL)","BIT 6,A",
"BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(HL)","BIT 7,A",
"RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(HL)","RES 0,A",
"RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(HL)","RES 1,A",
"RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(HL)","RES 2,A",
"RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(HL)","RES 3,A",
"RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(HL)","RES 4,A",
"RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(HL)","RES 5,A",
"RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(HL)","RES 6,A",
"RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(HL)","RES 7,A",
"SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(HL)","SET 0,A",
"SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(HL)","SET 1,A",
"SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(HL)","SET 2,A",
"SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(HL)","SET 3,A",
"SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(HL)","SET 4,A",
"SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(HL)","SET 5,A",
"SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(HL)","SET 6,A",
"SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(HL)","SET 7,A"
}, {
// Table 2: two byte instructions of form ED-XX
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
"IN B,(C)","OUT (C),B","SBC HL,BC","LD (@a),BC","NEG","RETN","IM 0","LD I,A",
"IN C,(C)","OUT (C),C","ADC HL,BC","LD BC,(@a)",NULL,"RETI",NULL,"LD R,A",
"IN D,(C)","OUT (C),D","SBC HL,DE","LD (@a),DE",NULL,NULL,"IM 1","LD A,I",
"IN E,(C)","OUT (C),E","ADC HL,DE","LD DE,(@a)",NULL,NULL,"IM 2","LD A,R",
"IN H,(C)","OUT (C),H","SBC HL,HL","LD (@a),HL",NULL,NULL,NULL,"RRD",
"IN L,(C)","OUT (C),L","ADC HL,HL","LD HL,(@a)",NULL,NULL,NULL,"RLD",
"IN F,(C)",NULL,"SBC HL,SP","LD (@a),SP",NULL,NULL,NULL,NULL,
"IN A,(C)","OUT (C),A","ADC HL,SP","LD SP,(@a)",NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
"LDI","CPI","INI","OUTI",NULL,NULL,NULL,NULL,
"LDD","CPD","IND","OUTD",NULL,NULL,NULL,NULL,
"LDIR","CPIR","INIR","OTIR",NULL,NULL,NULL,NULL,
"LDDR","CPDR","INDR","OTDR",NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
}, {
// Table 3: two byte instructions of form DD-XX or FD-XX
"NOP","LD BC,@a","LD (BC),A","INC BC","INC B","DEC B","LD B,@d","RLCA",
"EX AF,AF'","ADD I?,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,@d","RRCA",
"DJNZ @b","LD DE,@a","LD (DE),A","INC DE","INC D","DEC D","LD D,@d","RLA",
"JR @b","ADD I?,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,@d","RRA",
"JR NZ,@b","LD I?,@a","LD (@a),I?","INC I?","INC I?h","DEC I?h","LD I?h,@d","DAA",
"JR Z,@b","ADD I?,I?","LD I?,(@a)","DEC I?","INC I?l","DEC I?l","LD I?l,@d","CPL",
"JR NC,@b","LD SP,@a","LD (@a),A","INC SP","INC (I?+@d)","DEC (I?+@d)","LD (I?+@d),@d","SCF",
"JR C,@b","ADD I?,SP","LD A,(@a)","DEC SP","INC A","DEC A","LD A,@d","CCF",
"LD B,B","LD B,C","LD B,D","LD B,E","LD B,I?h","LD B,I?l","LD B,(I?+@d)","LD B,A",
"LD C,B","LD C,C","LD C,D","LD C,E","LD C,I?h","LD C,I?l","LD C,(I?+@d)","LD C,A",
"LD D,B","LD D,C","LD D,D","LD D,E","LD D,I?h","LD D,I?l","LD D,(I?+@d)","LD D,A",
"LD E,B","LD E,C","LD E,D","LD E,E","LD E,I?h","LD E,I?l","LD E,(I?+@d)","LD E,A",
"LD I?h,B","LD I?h,C","LD I?h,D","LD I?h,E","LD I?h,I?h","LD I?h,I?l","LD H,(I?+@d)","LD I?h,A",
"LD I?l,B","LD I?l,C","LD I?l,D","LD I?l,E","LD I?l,I?h","LD I?l,I?l","LD L,(I?+@d)","LD I?l,A",
"LD (I?+@d),B","LD (I?+@d),C","LD (I?+@d),D","LD (I?+@d),E","LD (I?+@d),H","LD (I?+@d),L","HALT","LD (I?+@d),A",
"LD A,B","LD A,C","LD A,D","LD A,E","LD A,I?h","LD A,I?l","LD A,(I?+@d)","LD A,A",
"ADD B","ADD C","ADD D","ADD E","ADD I?h","ADD I?l","ADD (I?+@d)","ADD A",
"ADC B","ADC C","ADC D","ADC E","ADC I?h","ADC I?l","ADC (I?+@d)","ADC,A",
"SUB B","SUB C","SUB D","SUB E","SUB I?h","SUB I?l","SUB (I?+@d)","SUB A",
"SBC B","SBC C","SBC D","SBC E","SBC I?h","SBC I?l","SBC (I?+@d)","SBC A",
"AND B","AND C","AND D","AND E","AND I?h","AND I?l","AND (I?+@d)","AND A",
"XOR B","XOR C","XOR D","XOR E","XOR I?h","XOR I?l","XOR (I?+@d)","XOR A",
"OR B","OR C","OR D","OR E","OR I?h","OR I?l","OR (I?+@d)","OR A",
"CP B","CP C","CP D","CP E","CP I?h","CP I?l","CP (I?+@d)","CP A",
"RET NZ","POP BC","JP NZ,@a","JP @a","CALL NZ,@a","PUSH BC","ADD @d","RST 00h",
"RET Z","RET","JP Z,@a","PFX_CB","CALL Z,@a","CALL @a","ADC @d","RST 08h",
"RET NC","POP DE","JP NC,@a","OUTA (@p)","CALL NC,@a","PUSH DE","SUB @d","RST 10h",
"RET C","EXX","JP C,@a","INA (@p)","CALL C,@a","PFX_DD","SBC @d","RST 18h",
"RET PO","POP I?","JP PO,@a","EX I?,(SP)","CALL PO,@a","PUSH I?","AND @d","RST 20h",
"RET PE","LD PC,I?","JP PE,@a","EX DE,I?","CALL PE,@a","PFX_ED","XOR @d","RST 28h",
"RET P","POP AF","JP P,@a","DI","CALL P,@a","PUSH AF","OR @d","RST 30h",
"RET M","LD SP,I?","JP M,@a","EI","CALL M,@a","PFX_FD","CP @d","RST 38h"
}, {
// Table 4: three byte instructions of form DD-CB-XX or FD-CB-XX
"RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (I?@o)","RLC A",
"RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (I?@o)","RRC A",
"RL B","RL C","RL D","RL E","RL H","RL L","RL (I?@o)","RL A",
"RR B","RR C","RR D","RR E","RR H","RR L","RR (I?@o)","RR A",
"SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (I?@o)","SLA A",
"SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (I?@o)","SRA A",
"SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (I?@o)","SLL A",
"SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (I?@o)","SRL A",
"BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(I?@o)","BIT 0,A",
"BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(I?@o)","BIT 1,A",
"BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(I?@o)","BIT 2,A",
"BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(I?@o)","BIT 3,A",
"BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(I?@o)","BIT 4,A",
"BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(I?@o)","BIT 5,A",
"BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(I?@o)","BIT 6,A",
"BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(I?@o)","BIT 7,A",
"RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(I?@o)","RES 0,A",
"RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(I?@o)","RES 1,A",
"RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(I?@o)","RES 2,A",
"RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(I?@o)","RES 3,A",
"RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(I?@o)","RES 4,A",
"RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(I?@o)","RES 5,A",
"RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(I?@o)","RES 6,A",
"RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(I?@o)","RES 7,A",
"SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(I?@o)","SET 0,A",
"SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(I?@o)","SET 1,A",
"SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(I?@o)","SET 2,A",
"SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(I?@o)","SET 3,A",
"SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(I?@o)","SET 4,A",
"SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(I?@o)","SET 5,A",
"SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(I?@o)","SET 6,A",
"SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(I?@o)","SET 7,A"
}
};
UINT8 CZ80Debug::ReadReg8(CCPUDebug *cpu, unsigned reg8)
{
return ((CZ80Debug*)cpu)->m_z80->GetReg8(reg8);
}
bool CZ80Debug::WriteReg8(CCPUDebug *cpu, unsigned reg8, UINT8 value)
{
return ((CZ80Debug*)cpu)->m_z80->SetReg8(reg8, value);
}
UINT16 CZ80Debug::ReadReg16(CCPUDebug *cpu, unsigned reg16)
{
return ((CZ80Debug*)cpu)->m_z80->GetReg16(reg16);
}
bool CZ80Debug::WriteReg16(CCPUDebug *cpu, unsigned reg16, UINT16 value)
{
return ((CZ80Debug*)cpu)->m_z80->SetReg16(reg16, value);
}
static const char *crGroup = "Control Regs";
static const char *irGroup = "Int/Refresh Regs";
static const char *r8Group = "8-Bit Regs";
static const char *r16Group = "16-Bit Regs";
static const char *srGroup = "Shadow Regs";
CZ80Debug::CZ80Debug(const char *name, CZ80 *z80) : CCPUDebug("Z80", name, 1, 4, false, 16, 4), m_z80(z80)
{
// Main registers
AddPCRegister ("PC", crGroup);
AddInt16Register ("SP", crGroup, Z80_REG16_SP, ReadReg16, WriteReg16);
AddStatus8Register("F", crGroup, Z80_REG8_F, "SZ.HPVNC", ReadReg8, WriteReg8);
AddStatus8Register("IFF", irGroup, Z80_REG8_IFF, "H.E.G21F", ReadReg8, WriteReg8);
AddInt8Register ("I", irGroup, Z80_REG8_I, ReadReg8, WriteReg8);
AddInt8Register ("R", irGroup, Z80_REG8_R, ReadReg8, WriteReg8);
AddInt8Register("A", r8Group, Z80_REG8_A, ReadReg8, WriteReg8);
AddInt8Register("B", r8Group, Z80_REG8_B, ReadReg8, WriteReg8);
AddInt8Register("C", r8Group, Z80_REG8_C, ReadReg8, WriteReg8);
AddInt8Register("D", r8Group, Z80_REG8_D, ReadReg8, WriteReg8);
AddInt8Register("E", r8Group, Z80_REG8_E, ReadReg8, WriteReg8);
AddInt16Register("AF", r16Group, Z80_REG16_AF, ReadReg16, WriteReg16);
AddInt16Register("BC", r16Group, Z80_REG16_BC, ReadReg16, WriteReg16);
AddInt16Register("DE", r16Group, Z80_REG16_DE, ReadReg16, WriteReg16);
AddInt16Register("HL", r16Group, Z80_REG16_HL, ReadReg16, WriteReg16);
AddInt16Register("IX", r16Group, Z80_REG16_IX, ReadReg16, WriteReg16);
AddInt16Register("IY", r16Group, Z80_REG16_IY, ReadReg16, WriteReg16);
AddInt16Register("AF'", srGroup, Z80_REG16_AF_, ReadReg16, WriteReg16);
AddInt16Register("BC'", srGroup, Z80_REG16_BC_, ReadReg16, WriteReg16);
AddInt16Register("DE'", srGroup, Z80_REG16_DE_, ReadReg16, WriteReg16);
AddInt16Register("HL'", srGroup, Z80_REG16_HL_, ReadReg16, WriteReg16);
// Exceptions
AddException("NMI", Z80_EX_NMI, "Non-Maskable Interrupt");
AddException("RST00", Z80_INT_RST_00, "RST 00h (IM0)");
AddException("RST08", Z80_INT_RST_08, "RST 08h (IM0)");
AddException("RST10", Z80_INT_RST_10, "RST 10h (IM0)");
AddException("RST18", Z80_INT_RST_18, "RST 18h (IM0)");
AddException("RST20", Z80_INT_RST_20, "RST 20h (IM0)");
AddException("RST28", Z80_INT_RST_28, "RST 28h (IM0)");
AddException("RST30", Z80_INT_RST_30, "RST 30h (IM0)");
AddException("RST38", Z80_INT_RST_38, "RST 38h (IM0)");
AddException("IRQ", Z80_IM1_IRQ, "Hardwired IRQ (IM1)");
AddException("VECTOR", Z80_IM2_VECTOR, "Interrupt Vectors (IM2)");
// Interrupts
for (unsigned vecNum = 0; vecNum < 256; vecNum++)
{
sprintf(m_vecIds[vecNum], "V%u", vecNum + 1);
sprintf(m_vecNames[vecNum], "Interrupt Vector #%u", vecNum + 1);
AddInterrupt(m_vecIds[vecNum], vecNum, m_vecNames[vecNum]);
}
// I/O ports
for (unsigned portNum = 0; portNum < 256; portNum++)
{
sprintf(m_portNames[portNum], "Port #%u", portNum);
AddPortIO(portNum, 1, m_portNames[portNum], "I/O Ports");
}
}
CZ80Debug::~CZ80Debug()
{
DetachFromCPU();
}
void CZ80Debug::AttachToCPU()
{
m_z80->AttachDebugger(this);
}
::IBus *CZ80Debug::AttachBus(::IBus *bus)
{
m_bus = bus;
return this;
}
void CZ80Debug::DetachFromCPU()
{
m_z80->DetachDebugger();
}
::IBus *CZ80Debug::DetachBus()
{
::IBus *bus = m_bus;
m_bus = NULL;
return bus;
}
UINT32 CZ80Debug::GetResetAddr()
{
return 0x0000;
}
bool CZ80Debug::UpdatePC(UINT32 pc)
{
return m_z80->SetReg16(Z80_REG16_PC, pc);
}
bool CZ80Debug::ForceException(CException *ex)
{
if (ex->code != Z80_EX_NMI)
{
// TODO
return false;
}
m_z80->TriggerNMI();
return true;
}
bool CZ80Debug::ForceInterrupt(CInterrupt *in)
{
// TODO
return false;
}
UINT64 CZ80Debug::ReadMem(UINT32 addr, unsigned dataSize)
{
if (dataSize == 1)
return m_bus->Read8(addr);
// TODO - byte swapping
return CCPUDebug::ReadMem(addr, dataSize);
}
bool CZ80Debug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data)
{
if (dataSize == 1)
{
m_bus->Write8(addr, (UINT8)data);
return true;
}
// TODO - byte swapping
return CCPUDebug::WriteMem(addr, dataSize, data);
}
UINT64 CZ80Debug::ReadPort(UINT16 portNum)
{
return m_bus->IORead8(portNum);
}
bool CZ80Debug::WritePort(UINT16 portNum, UINT64 data)
{
m_bus->IOWrite8(portNum, (UINT8)data);
return true;
}
int CZ80Debug::Disassemble(UINT32 addr, char *mnemonic, char *operands)
{
char tmpStr[128], valStr[50], *templ, *pos;
INT8 offs;
EOpFlags opFlags;
UINT16 dAddr = addr;
UINT16 tAddr;
char xyChr = '\0';
bool notJump = false;
// Get instruction template from opcode
UINT8 opcode = m_bus->Read8(dAddr++);
UINT8 nextCode;
switch (opcode)
{
case 0xCB:
templ = templates[1][m_bus->Read8(dAddr++)];
break;
case 0xED:
templ = templates[2][m_bus->Read8(dAddr++)];
break;
case 0xDD:
xyChr = 'X';
nextCode = m_bus->Read8(dAddr++);
if (nextCode == 0xCB)
{
offs = m_bus->Read8(dAddr++);
notJump = true;
templ = templates[4][m_bus->Read8(dAddr++)];
}
else
templ = templates[3][nextCode];
break;
case 0xFD:
xyChr = 'Y';
nextCode = m_bus->Read8(dAddr++);
if (nextCode == 0xCB)
{
offs = m_bus->Read8(dAddr++);
notJump = true;
templ = templates[4][m_bus->Read8(dAddr++)];
}
else
templ = templates[3][nextCode];
break;
default:
templ = templates[0][opcode];
break;
}
// If no template found, then instruction is invalid
if (templ == NULL)
return -1;
// See if instruction has any operands
if (pos = strchr(templ, ' '))
{
// If so, substitute all @ parameters in template operand
strncpy(mnemonic, templ, pos - templ);
mnemonic[pos - templ] = '\0';
operands[0] = '\0';
char *opPos = operands;
templ = pos + 1;
for (;;)
{
if (pos = strchr(templ, '@'))
{
strncpy(opPos, templ, pos - templ);
opPos[pos - templ] = '\0';
// Format data for parameter
switch (*(pos + 1))
{
case 'd': // ^h or *h
FormatData(valStr, 1, m_bus->Read8(dAddr++));
break;
case 'p': // *p
FormatPortNum(valStr, m_bus->Read8(dAddr++), true);
break;
case 'b': // @H
offs = m_bus->Read8(dAddr++);
tAddr = dAddr + offs;
opFlags = GetOpFlags(addr, opcode);
FormatJumpAddress(valStr, tAddr, opFlags);
break;
case 'o': // @h
if (notJump)
{
// Keep argument as +/-offs
strcat(operands, (offs&0x80 ? "-" : "+"));
FormatData(valStr, 1, (offs&0x80 ? 256 - offs : offs));
}
else
{
offs = m_bus->Read8(dAddr++);
tAddr = dAddr + offs;
FormatAddress(valStr, tAddr, true);
}
break;
case 'j': // #H
tAddr = m_bus->Read8(dAddr++) + (m_bus->Read8(dAddr++)<<8);
opFlags = GetOpFlags(addr, opcode);
FormatJumpAddress(valStr, tAddr, opFlags);
break;
case 'a': // #h
tAddr = m_bus->Read8(dAddr++) + (m_bus->Read8(dAddr++)<<8);
FormatAddress(valStr, tAddr, true);
break;
}
// Append formatted data and loop again to process any more @ parameters
strcat(opPos, valStr);
templ = pos + 2;
opPos += strlen(opPos);
}
else
{
// No more @ parameters found so append remainder of template operand and exit loop
strcat(opPos, templ);
break;
}
}
// Finally substitute ? parameter (X or Y)
if (pos = strchr(operands, '?'))
*pos = xyChr;
}
else
{
// Instruction has no operands
strcpy(mnemonic, templ);
operands[0] = '\0';
}
// Return instruction size
return dAddr - addr;
}
//int CZ80Debug::GetOpLength(UINT32 addr)
//{
// // Get instruction template from opcode
// UINT32 dAddr = addr;
// UINT8 opcode = m_bus->Read8(dAddr++);
// const char *templ;
// switch (opcode)
// {
// case 0xCB:
// templ = templates[1][m_bus->Read8(dAddr++)];
// break;
// case 0xED:
// templ = templates[2][m_bus->Read8(dAddr++)];
// break;
// case 0xDD:
// opcode = m_bus->Read8(dAddr++);
// if (opcode == 0xCB)
// {
// dAddr++;
// templ = templates[4][m_bus->Read8(dAddr++)];
// }
// else
// templ = templates[3][opcode];
// break;
// case 0xFD:
// opcode = m_bus->Read8(dAddr++);
// if (opcode == 0xCB)
// {
// dAddr++;
// templ = templates[4][m_bus->Read8(dAddr++)];
// }
// else
// templ = templates[3][opcode];
// break;
// default:
// templ = templates[0][opcode];
// break;
// }
//
// // If no template found, then instruction is invalid
// if (templ == NULL)
// return -1;
//
// // Return instruction size
// return dAddr - addr;
//}
EOpFlags CZ80Debug::GetOpFlags(UINT32 addr, UINT32 opcode)
{
if (opcode == 0xC3)
return JumpSimple; // JP
else if (opcode == 0xC2 || opcode == 0xCA || opcode == 0xD2 || opcode == 0xDA ||
opcode == 0xE2 || opcode == 0xEA || opcode == 0xF2 || opcode == 0xFA)
return (EOpFlags)(JumpSimple | Conditional); // JP NZ, JP Z, JP NC, JP C, JP PO, JP PE, JP P, JP M
else if (opcode == 0x18)
return (EOpFlags)(JumpSimple | Relative); // JR
else if (opcode == 0x20 || opcode == 0x28 || opcode == 0x30 || opcode == 0x38)
return (EOpFlags)(JumpSimple | Relative | Conditional); // JR NZ, JR Z, JR NC, JR C
else if (opcode == 0x10)
return (EOpFlags)(JumpLoop | Relative | Conditional); // DJNZ
else if (opcode == 0xCD)
return JumpSub; // CALL
else if (opcode == 0xC4 || opcode == 0xCC || opcode == 0xD4 || opcode == 0xDC ||
opcode == 0xE4 || opcode == 0xEC || opcode == 0xF4 || opcode == 0xFC)
return (EOpFlags)(JumpSub | Conditional); // CALL NZ, CALL Z, CALL NC, CALL C, CALL PO, CALL PE, CALL P, CALL M
else if (opcode == 0xC9)
return ReturnSub; // RET
else if (opcode == 0xC0 || opcode == 0xC8 || opcode == 0xD0 || opcode == 0xD8 ||
opcode == 0xE0 || opcode == 0xE8 || opcode == 0xF0 || opcode == 0xF8)
return (EOpFlags)(ReturnSub | Conditional); // RET NZ, RET Z, RET NC, RET C, RET PO, RET PE, RET P, RET M
else if (opcode == 0xC7 || opcode == 0xCF || opcode == 0xD7 || opcode == 0xD7 ||
opcode == 0xE7 || opcode == 0xEF || opcode == 0xF7 || opcode == 0xFF)
return (EOpFlags)(JumpEx | NotFixed); // RST 0, RST 8, RST 10H, RST 18H, RST 20H, RST 28H, RST 30H, RST 38H
else if (opcode == 0xED)
{
UINT8 nextCode = m_bus->Read8(addr + 1);
if (nextCode == 0x4D || nextCode == 0x45)
return ReturnEx; // RETI, RETN
}
else if (opcode == 0xE9 ||
opcode == 0xDD && m_bus->Read8(addr + 1) == 0xE9 ||
opcode == 0xFD && m_bus->Read8(addr + 1) == 0xE9)
return (EOpFlags)(JumpSimple | NotFixed); // JR (HL), JR (IX) or JR (IY)
return NormalOp;
}
bool CZ80Debug::GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr)
{
if (opcode == 0xC3 ||
opcode == 0xC2 || opcode == 0xCA || opcode == 0xD2 || opcode == 0xDA ||
opcode == 0xE2 || opcode == 0xEA || opcode == 0xF2 || opcode == 0xFA ||
opcode == 0xCD ||
opcode == 0xC4 || opcode == 0xCC || opcode == 0xD4 || opcode == 0xDC ||
opcode == 0xE4 || opcode == 0xEC || opcode == 0xF4 || opcode == 0xFC)
{
// JP, JP NZ, JP Z, JP NC, JP C, JP PO, JP PE, JP P, JP M,
// CALL, CALL NZ, CALL Z, CALL NC, CALL C, CALL PO, CALL PE, CALL P, CALL M
jumpAddr = (UINT32)(m_bus->Read8(addr + 1) + (m_bus->Read8(addr + 2)<<8));
return true;
}
else if (opcode == 0x18 || opcode == 0x20 || opcode == 0x28 || opcode == 0x30 || opcode == 0x38 || opcode == 0x10)
{
// JR, JR NZ, JR NC, JR C, DJNZ
INT8 offs = m_bus->Read8(addr + 1);
jumpAddr = addr + 2 + offs;
return true;
}
return false;
}
bool CZ80Debug::GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr)
{
if (opcode == 0xCD ||
opcode == 0xC4 || opcode == 0xCC || opcode == 0xD4 || opcode == 0xDC ||
opcode == 0xE4 || opcode == 0xEC || opcode == 0xF4 || opcode == 0xFC)
{
// CALL, CALL NZ, CALL Z, CALL NC, CALL C, CALL PO, CALL PE, CALL P, CALL M
retAddr = addr + 3;
return true;
}
else if (opcode == 0xC7 || opcode == 0xCF || opcode == 0xD7 || opcode == 0xD7 ||
opcode == 0xE7 || opcode == 0xEF || opcode == 0xF7 || opcode == 0xFF)
{
// RST 0, RST 8, RST 10H, RST 18H, RST 20H, RST 28H, RST 30H, RST 38H
retAddr = addr + 1;
}
return false;
}
bool CZ80Debug::GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr)
{
if (opcode == 0xC9 ||
opcode == 0xC0 || opcode == 0xC8 || opcode == 0xD0 || opcode == 0xD8 ||
opcode == 0xE0 || opcode == 0xE8 || opcode == 0xF0 || opcode == 0xF8)
{
// RET, RET NZ, RET Z, RET NC, RET C, RET PO, RET PE, RET P, RET M
UINT16 sp = m_z80->GetReg16(Z80_REG16_SP);
retAddr = (UINT32)m_bus->Read8(sp) + (((UINT32)m_bus->Read8(sp + 1))<<8);
return true;
}
else if (opcode == 0xED)
{
UINT8 nextCode = m_bus->Read8(addr + 1);
if (nextCode == 0x4D || nextCode == 0x45)
{
// RETI, RETN
UINT16 sp = m_z80->GetReg16(Z80_REG16_SP);
retAddr = (UINT32)m_bus->Read8(sp) + (((UINT32)m_bus->Read8(sp + 1))<<8);
return true;
}
}
return false;
}
bool CZ80Debug::GetHandlerAddr(CException *ex, UINT32 &handlerAddr)
{
switch (ex->code)
{
case Z80_EX_NMI: handlerAddr = 0x0066; return true; // Hardwired NMI vector
case Z80_INT_RST_00: handlerAddr = 0x0000; return true; // RST 0h
case Z80_INT_RST_08: handlerAddr = 0x0008; return true; // RST 8h
case Z80_INT_RST_10: handlerAddr = 0x0010; return true; // RST 10h
case Z80_INT_RST_18: handlerAddr = 0x0018; return true; // RST 18h
case Z80_INT_RST_20: handlerAddr = 0x0020; return true; // RST 20h
case Z80_INT_RST_28: handlerAddr = 0x0028; return true; // RST 28h
case Z80_INT_RST_30: handlerAddr = 0x0030; return true; // RST 30h
case Z80_INT_RST_38: handlerAddr = 0x0038; return true; // RST 38h
case Z80_IM1_IRQ: handlerAddr = 0x0038; return true; // Hardwired IRQ vector
default: return false;
}
}
bool CZ80Debug::GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr)
{
UINT8 iff = m_z80->GetReg8(Z80_REG8_IFF);
if (!(iff&0x04)) // IM2 mode
return false;
UINT8 i = m_z80->GetReg8(Z80_REG8_I);
handlerAddr = (((UINT32)i)<<8)|(((UINT32)in->code)&0xFF);
return true;
}
// IBus methods
UINT8 CZ80Debug::Read8(UINT32 addr)
{
UINT8 data = m_bus->Read8(addr);
CheckRead8(addr, data);
return data;
}
void CZ80Debug::Write8(UINT32 addr, UINT8 data)
{
m_bus->Write8(addr, data);
CheckWrite8(addr, data);
}
UINT8 CZ80Debug::IORead8(UINT32 portNum)
{
UINT8 data = m_bus->IORead8(portNum);
CheckPortInput(portNum, data);
return data;
}
void CZ80Debug::IOWrite8(UINT32 portNum, UINT8 data)
{
m_bus->IOWrite8(portNum, data);
CheckPortOutput(portNum, data);
}
}
#endif // SUPERMODEL_DEBUGGER
+119
View File
@@ -0,0 +1,119 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Z80KDebug.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_Z80DEBUG_H
#define INCLUDED_Z80DEBUG_H
#include "Debugger/CPUDebug.h"
#include "CPU/Bus.h"
#include "Types.h"
class CZ80;
namespace Debugger
{
/*
* CCPUDebug implementation for the Zilog Z80 emulator.
*/
class CZ80Debug : public CCPUDebug, public ::IBus
{
private:
static UINT8 ReadReg8(CCPUDebug *cpu, unsigned reg8);
static bool WriteReg8(CCPUDebug *cpu, unsigned reg8, UINT8 value);
static UINT16 ReadReg16(CCPUDebug *cpu, unsigned reg16);
static bool WriteReg16(CCPUDebug *cpu, unsigned reg16, UINT16 value);
CZ80 *m_z80;
::IBus *m_bus;
char m_vecIds[256][5];
char m_vecNames[256][22];
char m_portNames[256][10];
public:
CZ80Debug(const char *name, CZ80 *z80);
virtual ~CZ80Debug();
// CZ80Debug methods
void AttachToCPU();
::IBus *AttachBus(::IBus *bus);
void DetachFromCPU();
::IBus *DetachBus();
UINT32 GetResetAddr();
bool UpdatePC(UINT32 pc);
bool ForceException(CException *ex);
bool ForceInterrupt(CInterrupt *in);
UINT64 ReadMem(UINT32 addr, unsigned dataSize);
bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data);
UINT64 ReadPort(UINT16 portNum);
bool WritePort(UINT16 portNum, UINT64 data);
int Disassemble(UINT32 addr, char *mnemonic, char *operands);
//int GetOpLength(UINT32 addr);
EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode);
bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr);
bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr);
bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr);
bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr);
bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr);
// IBus methods
UINT8 Read8(UINT32 addr);
void Write8(UINT32 addr, UINT8 data);
UINT8 IORead8(UINT32 portNum);
void IOWrite8(UINT32 portNum, UINT8 data);
};
}
#endif // INCLUDED_Z80DEBUG_H
#endif // SUPERMODEL_DEBUGGER
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+740
View File
@@ -0,0 +1,740 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* CodeAnalyser.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "CodeAnalyser.h"
#include "CPUDebug.h"
#include "Label.h"
#include <cctype>
#include <string>
using namespace std;
namespace Debugger
{
CEntryPoint::CEntryPoint(const CEntryPoint &other) : addr(other.addr), autoFlag(other.autoFlag)
{
if (other.autoLabel != NULL)
{
strncpy(autoLabelStr, other.autoLabel, 254);
autoLabelStr[254] = '\0';
autoLabel = autoLabelStr;
}
else
autoLabel = NULL;
}
CEntryPoint::CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel) :
addr(eAddr), autoFlag(eAutoFlag)
{
if (eAutoLabel != NULL)
{
strncpy(autoLabelStr, eAutoLabel, 254);
autoLabelStr[254] = '\0';
autoLabel = autoLabelStr;
}
else
autoLabel = NULL;
}
CEntryPoint &CEntryPoint::operator=(const CEntryPoint &other)
{
addr = other.addr;
autoFlag = other.autoFlag;
if (other.autoLabel != NULL)
{
strncpy(autoLabelStr, other.autoLabel, 254);
autoLabelStr[254] = '\0';
autoLabel = autoLabelStr;
}
else
autoLabel = NULL;
return *this;
}
bool CEntryPoint::operator==(const CEntryPoint &other)
{
return addr == other.addr && autoFlag == other.autoFlag;
}
bool CEntryPoint::operator!=(const CEntryPoint &other)
{
return addr != other.addr || autoFlag != other.autoFlag;
}
const char *CAutoLabel::s_defaultLabelFmts[] = { "Entry%s", "Ex%s", "Int%s", "Jmp%s", "Loop%s", "Sub%s", NULL };
const unsigned CAutoLabel::numLabelFlags = sizeof(s_defaultLabelFmts) / sizeof(char*);
ELabelFlags CAutoLabel::GetLabelFlag(int index)
{
if (index < 0 || index >= numLabelFlags)
return LFNone;
return (ELabelFlags)(1 << index);
}
int CAutoLabel::GetFlagIndex(ELabelFlags flag)
{
switch (flag)
{
case LFEntryPoint: return 0;
case LFExcepHandler: return 1;
case LFInterHandler: return 2;
case LFJumpTarget: return 3;
case LFLoopPoint: return 4;
case LFSubroutine: return 5;
case LFUnseenCode: return 6;
default: return -1;
}
}
const char *CAutoLabel::GetFlagString(ELabelFlags flag)
{
switch (flag)
{
case LFEntryPoint: return "Entry Point";
case LFExcepHandler: return "Exception Handler";
case LFInterHandler: return "Interrupt Handler";
case LFJumpTarget: return "Jump Target";
case LFLoopPoint: return "Loop Point";
case LFSubroutine: return "Subroutine";
case LFUnseenCode: return "Unseen Code";
default: return "";
}
}
CAutoLabel::CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr) : CAddressRef(lCPU, lAddr), flags(LFNone), m_acquired(0)
{
memset(m_subLabels, NULL, sizeof(m_subLabels));
}
CAutoLabel::~CAutoLabel()
{
// Delete all sub-labels
for (int index = 0; index < numLabelFlags; index++)
{
if (m_subLabels[index] != NULL)
{
delete[] m_subLabels[index];
m_subLabels[index] = NULL;
}
}
}
void CAutoLabel::Acquire()
{
m_acquired++;
}
void CAutoLabel::Release()
{
if (--m_acquired == 0)
delete this;
}
void CAutoLabel::AddFlag(ELabelFlags flag, const char *subLabel)
{
int index = GetFlagIndex(flag);
if (index == -1)
return;
flags = (ELabelFlags)((unsigned)flags | (unsigned)flag);
if (subLabel != NULL)
{
size_t len = strlen(subLabel);
char *label = new char[len + 1];
strncpy(label, subLabel, len);
label[len] = '\0';
m_subLabels[index] = label;
}
else
m_subLabels[index] = CreateDefaultSubLabel(flag);
}
bool CAutoLabel::GetLabel(char *labelStr, ELabelFlags subFlags)
{
char *p = labelStr;
*p = '\0';
for (int index = 0; index < numLabelFlags; index++)
{
ELabelFlags flag = GetLabelFlag(index);
if (!(subFlags & flag))
continue;
const char *subLabel = m_subLabels[index];
if (subLabel == NULL)
continue;
if (p > labelStr)
{
(*p++) = '/';
*p = '\0';
}
strcat(p, subLabel);
p += strlen(subLabel);
*p = '\0';
}
return p > labelStr;
}
bool CAutoLabel::ContainsSubLabel(const char *subLabel)
{
for (int i = 0; i < numLabelFlags; i++)
{
if (m_subLabels[i] != NULL && stricmp(subLabel, m_subLabels[i]) == 0)
return true;
}
return false;
}
const char *CAutoLabel::CreateDefaultSubLabel(ELabelFlags flag)
{
int index = GetFlagIndex(flag);
if (index == -1)
return NULL;
const char *labelFmt = s_defaultLabelFmts[index];
if (labelFmt == NULL)
return NULL;
char addrStr[50];
cpu->debugger->FormatData(addrStr, Hex, (unsigned)(cpu->memBusWidth / 8), (UINT64)addr);
char *label = new char[255];
sprintf(label, labelFmt, addrStr);
return label;
}
CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser) : analyser(aAnalyser)
{
//
}
CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs) :
analyser(aAnalyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs),
m_seenIndices(aTotalIndices), m_validIndices(aTotalIndices), m_acquired(0)
{
//
}
CCodeAnalysis::CCodeAnalysis(CCodeAnalysis *oldAnalysis, vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs) :
analyser(oldAnalysis->analyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs),
m_seenIndices(oldAnalysis->m_seenIndices), m_validIndices(oldAnalysis->m_validIndices),
m_autoLabelsMap(oldAnalysis->m_autoLabelsMap), m_acquired(0), validIndexSet(oldAnalysis->validIndexSet)
{
for (map<unsigned,CAutoLabel*>::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++)
it->second->Acquire();
}
CCodeAnalysis::~CCodeAnalysis()
{
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
(*it)->Release();
autoLabels.clear();
m_autoLabelsMap.clear();
}
void CCodeAnalysis::Acquire()
{
m_acquired++;
}
void CCodeAnalysis::Release()
{
if (--m_acquired == 0)
delete this;
}
void CCodeAnalysis::FinishAnalysis()
{
for (map<UINT32,CAutoLabel*>::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++)
autoLabels.push_back(it->second);
}
bool CCodeAnalysis::IsAddrValid(UINT32 addr)
{
unsigned index;
if (!analyser->GetIndexOfAddr(addr, index))
return false;
return IsIndexValid(index);
}
bool CCodeAnalysis::GetNextValidAddr(UINT32 &addr)
{
unsigned index;
if (!analyser->GetIndexOfAddr(addr, index))
return false;
if (IsIndexValid(index))
return true;
set<unsigned>::iterator it = validIndexSet.lower_bound(index);
if (it == validIndexSet.end())
return false;
return analyser->GetAddrOfIndex(*it, addr);
}
bool CCodeAnalysis::IsIndexValid(unsigned index)
{
return index < m_validIndices.size() && m_validIndices[index];
}
bool CCodeAnalysis::GetNextValidIndex(unsigned &index)
{
if (IsIndexValid(index))
return true;
set<unsigned>::iterator it = validIndexSet.lower_bound(index);
if (it == validIndexSet.end())
return false;
index = *it;
return true;
}
bool CCodeAnalysis::HasSeenAddr(UINT32 addr)
{
unsigned index;
if (!analyser->GetIndexOfAddr(addr, index))
return false;
return HaveSeenIndex(index);
}
bool CCodeAnalysis::HaveSeenIndex(unsigned index)
{
return index < m_seenIndices.size() && m_seenIndices[index];
}
CAutoLabel *CCodeAnalysis::GetAutoLabel(UINT32 addr)
{
map<UINT32,CAutoLabel*>::iterator it = m_autoLabelsMap.find(addr);
if (it == m_autoLabelsMap.end())
return NULL;
return it->second;
}
CAutoLabel *CCodeAnalysis::GetAutoLabel(const char *subLabel)
{
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
{
if ((*it)->ContainsSubLabel(subLabel))
return *it;
}
return NULL;
}
vector<CAutoLabel*> CCodeAnalysis::GetAutoLabels(ELabelFlags flag)
{
vector<CAutoLabel*> matched;
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
{
if ((*it)->flags & flag)
matched.push_back(*it);
}
return matched;
}
CCodeAnalyser::CCodeAnalyser(CCPUDebug *aCPU) : cpu(aCPU), emptyAnalysis(this), analysis(&emptyAnalysis)
{
instrAlign = cpu->minInstrLen;
totalIndices = 0;
for (vector<CRegion*>::iterator it = cpu->regions.begin(); it != cpu->regions.end(); it++)
{
if (!(*it)->isCode)
continue;
m_codeRegions.push_back(*it);
totalIndices += (*it)->size / instrAlign;
m_indexBounds.push_back(totalIndices);
}
}
CCodeAnalyser::~CCodeAnalyser()
{
if (analysis != &emptyAnalysis)
analysis->Release();
}
void CCodeAnalyser::Reset()
{
CCodeAnalysis *oldAnalysis = analysis;
analysis = &emptyAnalysis;
if (oldAnalysis != &emptyAnalysis)
oldAnalysis->Release();
}
bool CCodeAnalyser::GetAddrOfIndex(unsigned index, UINT32 &addr)
{
unsigned regIndex = 0;
unsigned prevBound = 0;
for (vector<unsigned>::iterator it = m_indexBounds.begin(); it != m_indexBounds.end(); it++)
{
if (*it > index)
{
addr = m_codeRegions[regIndex]->addr + (UINT32)(index - prevBound) * instrAlign;
return true;
}
prevBound = *it;
regIndex++;
}
return false;
}
bool CCodeAnalyser::GetIndexOfAddr(UINT32 addr, unsigned &index)
{
unsigned regIndex = 0;
for (vector<CRegion*>::iterator it = m_codeRegions.begin(); it != m_codeRegions.end(); it++)
{
if ((*it)->addr <= addr && addr <= (*it)->addrEnd)
{
unsigned offset = (unsigned)((addr - (*it)->addr) / instrAlign);
index = (regIndex > 0 ? m_indexBounds[regIndex - 1] + offset : (unsigned)offset);
return true;
}
regIndex++;
}
return false;
}
void CCodeAnalyser::CheckEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, vector<CEntryPoint> &prevPoints,
bool &needsAnalysis, bool &reanalyse)
{
needsAnalysis = false;
// Gather entry points
GatherEntryPoints(entryPoints, unseenEntryAddrs, reanalyse);
if (reanalyse)
{
needsAnalysis = true;
return;
}
// Compare new entry points with previous ones
for (size_t i = 0; i < entryPoints.size(); i++)
{
// Check if have more than before
if (i >= prevPoints.size())
{
needsAnalysis = true;
return;
}
// Check if any have changed
if (entryPoints[i] != prevPoints[i])
{
// If entry points, exception handlers or interrupt handlers have changed, then force reanalysis
if (entryPoints[i].autoFlag == LFEntryPoint || prevPoints[i].autoFlag == LFEntryPoint ||
entryPoints[i].autoFlag == LFExcepHandler || prevPoints[i].autoFlag == LFExcepHandler ||
entryPoints[i].autoFlag == LFInterHandler || prevPoints[i].autoFlag == LFInterHandler)
reanalyse = true;
needsAnalysis = true;
return;
}
}
// Check if have less than before
if (entryPoints.size() < prevPoints.size())
{
// If so, force reanalysis
reanalyse = true;
needsAnalysis = true;
return;
}
}
void CCodeAnalyser::GatherEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, bool &reanalyse)
{
char labelStr[255];
UINT32 addr;
unsigned index;
entryPoints.clear();
reanalyse = false;
// Add reset address as main entry point
AddEntryPoint(entryPoints, cpu->GetResetAddr(), LFEntryPoint, "MainEntry");
// Add exception handlers as entry points
for (vector<CException*>::iterator it = cpu->exceps.begin(); it != cpu->exceps.end(); it++)
{
if (!cpu->GetHandlerAddr(*it, addr))
continue;
sprintf(labelStr, "Ex%s", (*it)->id);
AddEntryPoint(entryPoints, addr, LFExcepHandler, labelStr);
}
// Add interrupt handlers as entry points
for (vector<CInterrupt*>::iterator it = cpu->inters.begin(); it != cpu->inters.end(); it++)
{
if (!cpu->GetHandlerAddr(*it, addr))
continue;
sprintf(labelStr, "Int%s", (*it)->id);
AddEntryPoint(entryPoints, addr, LFInterHandler, labelStr);
}
// Add custom entry addresses
unsigned i = 0;
for (vector<UINT32>::iterator it = m_customEntryAddrs.begin(); it != m_customEntryAddrs.end(); it++)
{
sprintf(labelStr, "Custom%u", i++);
AddEntryPoint(entryPoints, *it, LFEntryPoint, labelStr);
}
// If current PC address is at an unseen location or at location that was previously invalid, then add address as unseen entry point
if (cpu->instrCount > 0 && GetIndexOfAddr(cpu->pc, index) && (!analysis->HaveSeenIndex(index) || !analysis->IsIndexValid(index)))
{
// If at location that was previously seen and was invalid, then force reanalysis (ie because code may have been modified)
if (analysis->HaveSeenIndex(index) && !analysis->IsIndexValid(index))
reanalyse = true;
// Check that address not already included in previous entry points
bool unseen = true;
for (vector<CEntryPoint>::iterator it = entryPoints.begin(); it != entryPoints.end(); it++)
{
if ((*it).addr == cpu->pc)
{
unseen = false;
break;
}
}
if (unseen && find(unseenEntryAddrs.begin(), unseenEntryAddrs.end(), cpu->pc) == unseenEntryAddrs.end())
unseenEntryAddrs.push_back(cpu->pc);
}
// Add unseen entry points
for (vector<UINT32>::iterator it = unseenEntryAddrs.begin(); it != unseenEntryAddrs.end(); it++)
AddEntryPoint(entryPoints, *it, LFUnseenCode, NULL);
}
void CCodeAnalyser::AddEntryPoint(vector<CEntryPoint> &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel)
{
CEntryPoint entryPoint(addr, autoFlag, autoLabel);
if (find(entryPoints.begin(), entryPoints.end(), entryPoint) == entryPoints.end())
entryPoints.push_back(entryPoint);
}
bool CCodeAnalyser::NeedsAnalysis()
{
vector<CEntryPoint> entryPoints;
vector<UINT32> unseenEntryAddrs(analysis->m_unseenEntryAddrs);
bool needsAnalysis;
bool reanalyse;
CheckEntryPoints(entryPoints, unseenEntryAddrs, analysis->m_entryPoints, needsAnalysis, reanalyse);
return needsAnalysis;
}
bool CCodeAnalyser::AnalyseCode()
{
m_abortAnalysis = false;
CCodeAnalysis *oldAnalysis = analysis;
vector<CEntryPoint> entryPoints;
vector<UINT32> unseenEntryAddrs(oldAnalysis->m_unseenEntryAddrs);
bool needsAnalysis;
bool reanalyse;
CheckEntryPoints(entryPoints, unseenEntryAddrs, oldAnalysis->m_entryPoints, needsAnalysis, reanalyse);
if (!needsAnalysis)
return false;
CCodeAnalysis *newAnalysis;
if (reanalyse || oldAnalysis == &emptyAnalysis)
newAnalysis = new CCodeAnalysis(this, totalIndices, entryPoints, unseenEntryAddrs);
else
newAnalysis = new CCodeAnalysis(oldAnalysis, entryPoints, unseenEntryAddrs);
newAnalysis->Acquire();
for (vector<CEntryPoint>::iterator it = newAnalysis->m_entryPoints.begin(); it != newAnalysis->m_entryPoints.end(); it++)
{
AddFlagToAddr(newAnalysis->m_autoLabelsMap, it->addr, it->autoFlag, it->autoLabel);
AnalyseCode(newAnalysis->m_seenIndices, newAnalysis->m_validIndices, newAnalysis->validIndexSet, newAnalysis->m_autoLabelsMap, it->addr);
}
newAnalysis->FinishAnalysis();
if (m_abortAnalysis)
{
newAnalysis->Release();
return false;
}
analysis = newAnalysis;
if (oldAnalysis != &emptyAnalysis)
oldAnalysis->Release();
cpu->debugger->AnalysisUpdated(this);
return true;
}
void CCodeAnalyser::AnalyseCode(vector<bool> &seenIndices, vector<bool> &validIndices, set<unsigned> &validIndexSet,
map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr)
{
if (m_abortAnalysis)
return;
unsigned index;
if (!GetIndexOfAddr(addr, index) || seenIndices[index])
return;
CRegion *region = cpu->GetRegion(addr);
if (region == NULL || !region->isCode)
return;
set<unsigned>::iterator setIt = validIndexSet.end();
unsigned startIndex = index;
do
{
if (m_abortAnalysis)
return;
// Flag that have seen this address index
seenIndices[index] = true;
// If unit is not valid (ie doesn't disassemble) then code block must be invalid (TODO - invalidate whole code block?)
int codesLen = cpu->GetOpLength(addr);
if (codesLen <= 0)
return;
validIndices[index] = true;
if (setIt != validIndexSet.end())
setIt = validIndexSet.insert(setIt, index);
else
setIt = validIndexSet.insert(index).first;
UINT32 opcode = cpu->GetOpcode(addr);
EOpFlags opFlags = cpu->GetOpFlags(addr, opcode);
// See if instruction is jump
if (opFlags & (JumpSimple|JumpLoop|JumpSub))
{
// If so, see if address is valid (ie known at disassemble time)
UINT32 jumpAddr;
if (cpu->GetJumpAddr(addr, opcode, jumpAddr))
{
// If so, add flags to jump address and analyse destination code block too
if (opFlags & JumpSub) AddFlagToAddr(autoLabelsMap, jumpAddr, LFSubroutine, NULL);
else if (opFlags & JumpLoop) AddFlagToAddr(autoLabelsMap, jumpAddr, LFLoopPoint, NULL);
else AddFlagToAddr(autoLabelsMap, jumpAddr, LFJumpTarget, NULL);
AnalyseCode(seenIndices, validIndices, validIndexSet, autoLabelsMap, jumpAddr);
}
}
// Finish if instruction terminates code block (ie is not conditional and is either a non-returning jump, a return or some sort
// of reset/halting instruction)
if (!(opFlags & Conditional) && (opFlags & (JumpSimple|JumpLoop|ReturnEx|ReturnSub|HaltExec)))
return;
// Move to next index
index += (unsigned)codesLen / instrAlign;
// If reach end of address indices, code block must be invalid (TODO - invalidate whole code block?)
if (index >= totalIndices)
return;
// Move to next address
addr += (UINT32)codesLen;
// If move between regions, check new region is valid
if (addr > region->addrEnd)
{
region = cpu->GetRegion(addr);
if (region == NULL || !region->isCode) // (TODO - invalidate whole code block?)
return;
}
}
while (!seenIndices[index]);
}
void CCodeAnalyser::AddFlagToAddr(map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr, ELabelFlags flag, const char *subLabel)
{
if (flag == LFNone)
return;
map<UINT32,CAutoLabel*>::iterator it = autoLabelsMap.find(addr);
CAutoLabel *label;
if (it == autoLabelsMap.end())
{
label = new CAutoLabel(cpu, addr);
label->Acquire();
autoLabelsMap[addr] = label;
}
else
label = it->second;
label->AddFlag(flag, subLabel);
}
void CCodeAnalyser::AbortAnalysis()
{
m_abortAnalysis = true;
}
void CCodeAnalyser::ClearCustomEntryAddrs()
{
m_customEntryAddrs.clear();
}
void CCodeAnalyser::AddCustomEntryAddr(UINT32 entryAddr)
{
if (find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr) == m_customEntryAddrs.end())
m_customEntryAddrs.push_back(entryAddr);
}
bool CCodeAnalyser::RemoveCustomEntryAddr(UINT32 entryAddr)
{
vector<UINT32>::iterator it = find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr);
if (it == m_customEntryAddrs.end())
return false;
m_customEntryAddrs.erase(it);
return true;
}
#ifdef DEBUGGER_HASBLOCKFILE
bool CCodeAnalyser::LoadState(CBlockFile *state)
{
// Load custom entry addresses
char blockStr[255];
sprintf(blockStr, "%s.entryaddrs", cpu->name);
if (state->FindBlock(blockStr) == OKAY)
{
m_customEntryAddrs.clear();
UINT32 numAddrs;
state->Read(&numAddrs, sizeof(numAddrs));
for (UINT32 i = 0; i < numAddrs; i++)
{
UINT32 addr;
state->Read(&addr, sizeof(addr));
m_customEntryAddrs.push_back(addr);
}
}
return true;
}
bool CCodeAnalyser::SaveState(CBlockFile *state)
{
// Save custom entry addresses
char blockStr[255];
sprintf(blockStr, "%s.entryaddrs", cpu->name);
state->NewBlock(blockStr, __FILE__);
UINT32 numAddrs = m_customEntryAddrs.size();
state->Write(&numAddrs, sizeof(numAddrs));
for (UINT32 i = 0; i < numAddrs; i++)
{
UINT32 addr = m_customEntryAddrs[i];
state->Write(&addr, sizeof(addr));
}
return true;
}
#endif // DEBUGGER_HASBLOCKFILE
}
#endif // SUPERMODEL_DEBUGGER
+262
View File
@@ -0,0 +1,262 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* CodeAnalyser.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_CODEANALYSER_H
#define INCLUDED_CODEANALYSER_H
#include <stdio.h>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include "Types.h"
#include "Debugger.h"
#include "AddressTable.h"
#ifdef DEBUGGER_HASBLOCKFILE
#include "BlockFile.h"
#endif // DEBUGGER_HASBLOCKFILE
namespace Debugger
{
class CCPUDebug;
class CRegion;
class CLabel;
enum ELabelFlags
{
LFNone = 0,
LFEntryPoint = 1,
LFExcepHandler = 2,
LFInterHandler = 4,
LFJumpTarget = 8,
LFLoopPoint = 16,
LFSubroutine = 32,
LFUnseenCode = 64,
LFAll = (LFEntryPoint | LFExcepHandler | LFInterHandler | LFJumpTarget | LFSubroutine | LFLoopPoint | LFUnseenCode)
};
/*
* Class that represents an entry point into a program's code in memory, eg a CPU's reset address or exception handler's address etc.
*/
class CEntryPoint
{
private:
char autoLabelStr[255];
public:
UINT32 addr;
ELabelFlags autoFlag;
const char *autoLabel;
CEntryPoint(const CEntryPoint &other);
CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel);
CEntryPoint &operator=(const CEntryPoint &other);
bool operator==(const CEntryPoint &other);
bool operator!=(const CEntryPoint &other);
};
/*
* Class that represents an auto-generated label that resulted from code analysis, eg a known entry point or the destination of a subroutine call.
*/
class CAutoLabel : public CAddressRef
{
private:
static const char *s_defaultLabelFmts[];
const char *m_subLabels[7];
unsigned m_acquired;
const char *CreateDefaultSubLabel(ELabelFlags flag);
public:
static const unsigned numLabelFlags;
static ELabelFlags GetLabelFlag(int index);
static int GetFlagIndex(ELabelFlags flag);
static const char *GetFlagString(ELabelFlags flag);
ELabelFlags flags;
CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr);
~CAutoLabel();
void Acquire();
void Release();
void AddFlag(ELabelFlags flag, const char *subLabel);
bool GetLabel(char *label, ELabelFlags flag = LFAll);
bool ContainsSubLabel(const char *subLabel);
};
class CCodeAnalyser;
/*
* Class that holds the results of having analysed the program code.
*/
class CCodeAnalysis
{
friend class CCodeAnalyser;
private:
std::vector<CEntryPoint> m_entryPoints;
std::vector<UINT32> m_unseenEntryAddrs;
std::vector<bool> m_seenIndices;
std::vector<bool> m_validIndices;
std::map<UINT32,CAutoLabel*> m_autoLabelsMap;
unsigned m_acquired;
CCodeAnalysis(CCodeAnalyser *aAnalyser);
CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, std::vector<CEntryPoint> &entryPoints, std::vector<UINT32> &m_unseenEntryAddrs);
CCodeAnalysis(CCodeAnalysis *oldAnalysis, std::vector<CEntryPoint> &entryPoints, std::vector<UINT32> &m_unseenEntryAddrs);
void FinishAnalysis();
public:
CCodeAnalyser *analyser;
std::set<unsigned> validIndexSet;
std::vector<CAutoLabel*> autoLabels;
~CCodeAnalysis();
void Acquire();
void Release();
bool IsAddrValid(UINT32 addr);
bool GetNextValidAddr(UINT32 &addr);
bool IsIndexValid(unsigned index);
bool GetNextValidIndex(unsigned &index);
bool HasSeenAddr(UINT32 addr);
bool HaveSeenIndex(unsigned index);
CAutoLabel *GetAutoLabel(UINT32 addr);
CAutoLabel *GetAutoLabel(const char *subLabel);
std::vector<CAutoLabel*> GetAutoLabels(ELabelFlags flag);
};
/*
* Class that analyses a program's code to work out all the reachable program locations from a given set of entry points into the code.
* During the analysis, it keeps track of valid memory locations (to help with the disassembly of code for CPUs with variable-length
* instruction sets) and generates a set of auto-labels that identify places of interest such as subroutines, jump destinations and
* exception handlers etc.
* This sort of analysis works well for static addressing modes but not so well for dynamic address referencing or self-modifying code.
* To allow for the latter cases the analyser updates its analysis whenever it encounters an unseen memory location or it sees
* that code has changed from a previous inspection.
*/
class CCodeAnalyser
{
private:
std::vector<CRegion*> m_codeRegions;
std::vector<unsigned> m_indexBounds;
std::vector<UINT32> m_customEntryAddrs;
bool m_abortAnalysis;
void CheckEntryPoints(std::vector<CEntryPoint> &entryPoints, std::vector<UINT32> &unseenEntryAddrs, std::vector<CEntryPoint> &prevPoints,
bool &needsAnalysis, bool &reanalyse);
void GatherEntryPoints(std::vector<CEntryPoint> &entryPoints, std::vector<UINT32> &unseenEntryAddrs, bool &reanalyse);
void AddEntryPoint(std::vector<CEntryPoint> &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel);
void AnalyseCode(std::vector<bool> &seenIndices, std::vector<bool> &validIndices, std::set<unsigned> &validIndexSet, std::map<UINT32, CAutoLabel*> &autoLabelsMap, UINT32 addr);
void AddFlagToAddr(std::map<UINT32, CAutoLabel*> &autoLabelsMap, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel);
public:
CCPUDebug *cpu;
CCodeAnalysis emptyAnalysis;
CCodeAnalysis *analysis;
unsigned instrAlign;
unsigned totalIndices;
CCodeAnalyser(CCPUDebug *aCPU);
~CCodeAnalyser();
void Reset();
bool GetAddrOfIndex(unsigned index, UINT32 &addr);
bool GetIndexOfAddr(UINT32 addr, unsigned &index);
//
// Methods to check, run or abort analysis
//
bool NeedsAnalysis();
bool AnalyseCode();
void AbortAnalysis();
//
// Methods to manipulate custom entry addresses
//
void ClearCustomEntryAddrs();
void AddCustomEntryAddr(UINT32 entryAddr);
bool RemoveCustomEntryAddr(UINT32 entryAddr);
#ifdef DEBUGGER_HASBLOCKFILE
bool LoadState(CBlockFile *state);
bool SaveState(CBlockFile *state);
#endif // DEBUGGER_HASBLOCKFILE
};
}
#endif // INCLUDED_CODEANALYSER_H
#endif // SUPERMODEL_DEBUGGER
File diff suppressed because it is too large. Load diff
+193
View File
@@ -0,0 +1,193 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* ConsoleDebugger.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_CONSOLEDEBUGGER_H
#define INCLUDED_CONSOLEDEBUGGER_H
#include "Debugger.h"
#include "CodeAnalyser.h"
#include "Types.h"
#include <stdarg.h>
#include <stdio.h>
#define NUM_LISTAUTOLABELS (sizeof(s_listAutoLabels) / sizeof(ELabelFlags))
namespace Debugger
{
class CCPUDebug;
class CException;
class CWatch;
class CBreakpoint;
class CRegister;
class CRegMonitor;
/*
* Base class for a console-based debugger.
* TODO - a lot of commands are not yet implemented/missing and the main loop is a big ugly mess :-) !
*/
class CConsoleDebugger : public CDebugger
{
private:
static ELabelFlags s_listAutoLabels[];
CCPUDebug *m_cpu;
bool m_nextFrame;
unsigned m_nextFrameCount;
UINT32 m_listDism;
UINT32 m_listMem;
bool m_analyseCode;
EFormat m_addrFmt;
EFormat m_portFmt;
EFormat m_dataFmt;
bool m_showLabels;
bool m_labelsOverAddr;
bool m_showOpCodes;
unsigned m_memBytesPerRow;
FILE *m_file;
protected:
void Read(char *str, size_t maxLen);
void Print(const char *fmtStr, ...);
void Error(const char *fmtStr, ...);
void PrintVL(const char *fmtStr, va_list vl);
void Flush();
bool CheckToken(const char *token, const char *simple, const char *full);
bool CheckToken(const char *token, const char *simple, const char *full, char *modifier, size_t modSize, const char *defaultMod);
void Truncate(char *dst, size_t maxLen, const char *src);
void UpperFirst(char *dst, const char *src);
void FormatOpCodes(char *str, int addr, int codesLen);
bool GetLabelText(char *str, int maxLen, UINT32 addr);
bool SetBoolConfig(const char *str, bool &cfg);
bool SetNumConfig(const char *str, unsigned &cfg);
const char *GetFmtConfig(EFormat fmt);
bool SetFmtConfig(const char *str, EFormat &cfg);
bool ParseAddress(const char *str, UINT32 *addr);
const char *GetDataSizeStr(unsigned dataSize, bool shortName);
bool ParseDataSize(const char *str, unsigned &dataSize);
bool ParseFormat(const char *str, EFormat &fmt);
bool ParseCPU(const char *str, CCPUDebug *&cpu);
bool ParseRegister(const char *str, CRegister *&reg);
void ListCPUs();
void ListRegisters();
void ListExceptions();
void ListInterrupts();
void ListIOs();
void ListRegions();
void ListLabels(bool customLabels, ELabelFlags autoLabelFlags);
void GetAllMemWatches(std::vector<CWatch*> &watches);
int GetIndexOfMemWatch(CWatch *watch);
void ListMemWatches();
void GetAllPortWatches(std::vector<CWatch*> &watches);
int GetIndexOfPortWatch(CWatch *watch);
void ListPortWatches();
void ListBreakpoints();
void ListMonitors();
UINT32 ListDisassembly(UINT32 start, UINT32 end, unsigned numInstrs);
UINT32 ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow);
virtual void ApplyConfig();
virtual void Attached();
virtual void Detaching();
virtual void AnalysisUpdated(CCodeAnalyser *analyser);
virtual void ExceptionTrapped(CException *ex);
virtual void InterruptTrapped(CInterrupt *ex);
virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead);
virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput);
virtual void BreakpointReached(CBreakpoint *bp);
virtual void MonitorTriggered(CRegMonitor *regMon);
virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason);
virtual void WaitCommand(CCPUDebug *cpu);
virtual bool ProcessToken(const char *token, const char *cmd);
virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl);
public:
CConsoleDebugger();
void Attach();
void Detach();
virtual void Poll();
};
}
#endif // INCLUDED_CONSOLEDEBUGGER_H
#endif // SUPERMODEL_DEBUGGER
+527
View File
@@ -0,0 +1,527 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Debugger.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Supermodel.h"
#include "Debugger.h"
#include "CPUDebug.h"
#include <cctype>
#include <string>
using namespace std;
namespace Debugger
{
unsigned CDebugger::GetDataSize(UINT64 data)
{
if (data <= 0xFFULL) return 1;
else if (data <= 0xFFFFULL) return 2;
else if (data <= 0xFFFFFFULL) return 3;
else if (data <= 0xFFFFFFFFULL) return 4;
else if (data <= 0xFFFFFFFFFFULL) return 5;
else if (data <= 0xFFFFFFFFFFFFULL) return 6;
else if (data <= 0xFFFFFFFFFFFFFFULL) return 7;
else return 8;
}
const char *CDebugger::GetSizeString(unsigned dataSize)
{
switch (dataSize)
{
case 1: return "8-bit";
case 2: return "16-bit";
case 3: return "24-bit";
case 4: return "32-bit";
case 5: return "40-bit";
case 6: return "48-bit";
case 7: return "56-bit";
case 8: return "64-bit";
default: return "";
}
}
UINT64 CDebugger::MaskData(unsigned dataSize, UINT64 data)
{
switch (dataSize)
{
case 0: return 0;
case 1: return data&0xFFULL;
case 2: return data&0xFFFFULL;
case 3: return data&0xFFFFFFULL;
case 4: return data&0xFFFFFFFFULL;
case 5: return data&0xFFFFFFFFFFULL;
case 6: return data&0xFFFFFFFFFFFFULL;
case 7: return data&0xFFFFFFFFFFFFFFULL;
default: return data;
}
}
UINT64 CDebugger::GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize)
{
if (addr == slotAddr && dataSize == slotDataSize)
{
// Data perfectly aligned with slot
return MaskData(slotDataSize, data);
}
unsigned overlap, shift;
if (addr >= slotAddr)
{
// Data starts after slot beginning
overlap = slotAddr + slotDataSize - addr;
if (overlap < dataSize)
{
// Data ends after slot end
shift = dataSize - overlap;
return MaskData(overlap, data>>(8 * shift));
}
else
{
// Data falls completely inside slot
shift = overlap - dataSize;
return MaskData(dataSize, data)<<(8 * shift);
}
}
else
{
// Data starts before slot beginning
overlap = addr + dataSize - slotAddr;
if (overlap < slotDataSize)
{
// Data ends before slot end
shift = slotDataSize - overlap;
return MaskData(overlap, data)<<(8 * shift);
}
else
{
// Slot lies completely inside data
shift = overlap - slotDataSize;
return MaskData(slotDataSize, data>>(8 * shift));
}
}
}
bool CDebugger::ParseInt(const char *str, int *val)
{
return sscanf(str, "%d", val) == 1;
}
CDebugger::CDebugger() : m_exit(false), m_pause(false), m_break(false), m_breakUser(false),
frameCount(0), logDebug(true), logInfo(true), logError(true)
{
#ifdef DEBUGGER_HASTHREAD
m_mutex = CThread::CreateMutex();
m_primaryCPU = NULL;
#endif // DEBUGGER_HASTHREAD
}
CDebugger::~CDebugger()
{
DeleteCPUs();
}
void CDebugger::DeleteCPUs()
{
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
delete *it;
cpus.clear();
}
void CDebugger::AddCPU(CCPUDebug *cpu)
{
cpu->AttachToDebugger(this);
cpus.push_back(cpu);
if (m_break)
cpu->ForceBreak(m_breakUser);
}
void CDebugger::RemoveCPU(CCPUDebug *cpu)
{
cpu->DetachFromDebugger(this);
vector<CCPUDebug*>::iterator it = find(cpus.begin(), cpus.end(), cpu);
if (it != cpus.end())
cpus.erase(it);
}
CCPUDebug *CDebugger::GetCPU(const char *name)
{
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if (stricmp(name, (*it)->name) == 0)
return *it;
}
return NULL;
}
void CDebugger::SetExit()
{
m_exit = true;
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
//(*it)->RemoveAllExceptionTraps();
(*it)->RemoveAllMemWatches();
(*it)->RemoveAllIOWatches();
(*it)->RemoveAllBreakpoints();
(*it)->RemoveAllRegMonitors();
(*it)->SetContinue();
}
}
void CDebugger::SetPause(bool pause)
{
m_pause = pause;
}
void CDebugger::ForceBreak(bool user)
{
m_break = true;
m_breakUser = user;
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->ForceBreak(user);
}
void CDebugger::ClearBreak()
{
m_break = false;
m_breakUser = false;
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->ClearBreak();
}
#ifdef DEBUGGER_HASTHREAD
bool CDebugger::MakePrimary(CCPUDebug *cpu)
{
m_mutex->Lock();
bool isPrimary = m_primaryCPU == NULL || m_primaryCPU == cpu;
if (isPrimary)
m_primaryCPU = cpu;
m_mutex->Unlock();
if (isPrimary)
{
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it) != m_primaryCPU)
(*it)->ForceWait();
}
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it) != m_primaryCPU)
(*it)->WaitForHalt();
}
}
return isPrimary;
}
void CDebugger::ReleasePrimary()
{
m_mutex->Lock();
m_primaryCPU = NULL;
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it) == m_primaryCPU)
(*it)->ClearBreak();
else
(*it)->ClearWait();
}
m_mutex->Unlock();
}
#endif // DEBUGGER_HASTHREAD
void CDebugger::Attach()
{
AddCPUs();
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->AttachToCPU();
}
void CDebugger::Detach()
{
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->DetachFromCPU();
DeleteCPUs();
}
void CDebugger::Reset()
{
frameCount = 0;
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->DebuggerReset();
}
void CDebugger::Poll()
{
frameCount++;
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->DebuggerPolled();
}
void CDebugger::PrintEvent(CCPUDebug *cpu, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
Log(cpu, NULL, fmt, vl);
va_end(vl);
}
#ifdef DEBUGGER_HASLOGGER
void CDebugger::DebugLog(const char *fmt, va_list vl)
{
if (logDebug)
Log(NULL, "Debug", fmt, vl);
}
void CDebugger::InfoLog(const char *fmt, va_list vl)
{
if (logInfo)
Log(NULL, "Info", fmt, vl);
}
void CDebugger::ErrorLog(const char *fmt, va_list vl)
{
if (logError)
Log(NULL, "Error", fmt, vl);
}
#endif // DEBUGGER_HASLOGGER
bool CDebugger::ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data)
{
unsigned i;
char upperStr[255];
const char *p;
switch (format)
{
case Hex:
case Hex0x:
case HexDollar:
case HexPostH:
for (i = 0; str[i] && i < 254; i++)
upperStr[i] = toupper(str[i]);
upperStr[i] = '\0';
if (sscanf(upperStr, "%llX", data) != 1 &&
sscanf(upperStr, "0x%llX", data) != 1 &&
sscanf(upperStr, "$%llX", data) != 1 &&
sscanf(upperStr, "%llXH", data) != 1)
return false;
break;
case Decimal:
if (sscanf(str, "%llu", data) != 1)
return false;
break;
case Binary:
p = str;
while (*p != '\0' && *p != '0' && *p != '1')
p++;
if (*p == '\0')
return false;
*data = 0;
for (i = 0; i < dataSize * 8; i++)
{
if (*p == '1')
*data |= 1;
else if (*p != '0')
break;
*data <<= 1;
p++;
}
break;
case ASCII:
// TODO
return false;
default:
return false;
}
switch (dataSize)
{
case 0: return 0;
case 1: return *data <= (UINT64)0xFF;
case 2: return *data <= (UINT64)0xFFFF;
case 3: return *data <= (UINT64)0xFFFFFF;
case 4: return *data <= (UINT64)0xFFFFFFFF;
case 5: return *data <= (UINT64)0xFFFFFFFFFF;
case 6: return *data <= (UINT64)0xFFFFFFFFFFFF;
case 7: return *data <= (UINT64)0xFFFFFFFFFFFFFF;
default: return true;
}
}
void CDebugger::FormatData(char *str, EFormat format, unsigned dataSize, INT64 data)
{
if (data >= 0)
FormatData(str, format, dataSize, (UINT64)data);
else
{
*str++ = '-';
FormatData(str, format, dataSize, (UINT64)(data >= 0 ? data : -data)); // No portable abs64 available
}
}
void CDebugger::FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data)
{
data = MaskData(dataSize, data);
unsigned i;
switch (format)
{
case Hex: sprintf(str, "%0*llX", (int)dataSize * 2, data); break;
case Hex0x: sprintf(str, "0x%0*llX", (int)dataSize * 2, data); break;
case HexDollar: sprintf(str, "$%0*llX", (int)dataSize * 2, data); break;
case HexPostH: sprintf(str, "%0*llXh", (int)dataSize * 2, data); break;
case Decimal: sprintf(str, "%llu", data); break;
case Binary:
str += dataSize * 8;
*str-- = '\0';
for (i = 0; i < dataSize * 8; i++)
{
*str-- = ((data&1) ? '1' : '0');
data >>= 1;
}
break;
case ASCII:
for (i = 0; i < dataSize; i++)
{
*str++ = (char)(data & 0xFF);
data >>= 8;
}
*str++ = '\0';
break;
default:
str[0] = '\0';
break;
}
}
bool CDebugger::HasState()
{
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it)->labels.size() + (*it)->comments.size() > 0)
return true;
}
return false;
}
bool CDebugger::LoadState(const char *fileName)
{
#ifdef DEBUGGER_HASBLOCKFILE
// Open file and find header
CBlockFile state;
if (state.Load(fileName) != OKAY)
return false;
if (state.FindBlock("Debugger State") != OKAY)
{
state.Close();
return false;
}
// Check version in header matches
unsigned version;
state.Read(&version, sizeof(version));
if (version != DEBUGGER_STATEFILE_VERSION)
{
state.Close();
return false;
}
// Load debugger state
if (!LoadState(&state))
{
state.Close();
return false;
}
// Load state for each CPU
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if (!(*it)->LoadState(&state))
{
state.Close();
return false;
}
}
state.Close();
return true;
#else
return false;
#endif // DEBUGGER_HASBLOCKFILE
}
bool CDebugger::SaveState(const char *fileName)
{
#ifdef DEBUGGER_HASBLOCKFILE
// Create file with header
CBlockFile state;
if (state.Create(fileName, "Debugger State", __FILE__) != OKAY)
return false;
// Write out version in header
unsigned version = DEBUGGER_STATEFILE_VERSION;
state.Write(&version, sizeof(version));
// Save debugger state
if (!SaveState(&state))
{
state.Close();
return false;
}
// Save state for each CPU
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if (!(*it)->SaveState(&state))
return false;
}
state.Close();
return true;
#else
return false;
#endif // DEBUGGER_HASBLOCKFILE
}
#ifdef DEBUGGER_HASBLOCKFILE
bool CDebugger::LoadState(CBlockFile *state)
{
return true;
}
bool CDebugger::SaveState(CBlockFile *state)
{
return true;
}
#endif
}
#endif // SUPERMODEL_DEBUGGER
+275
View File
@@ -0,0 +1,275 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Debugger.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_DEBUGGER_H
#define INCLUDED_DEBUGGER_H
#include <stdio.h>
#include <stdarg.h>
#include <vector>
#include <algorithm>
#include "Types.h"
//#if defined(SUPERMODEL_WIN32) || defined(SUPERMODEL_UNIX) || defined(SUPERMODEL_OSX)
#define DEBUGGER_HASBLOCKFILE
#define DEBUGGER_HASLOGGER
#define DEBUGGER_HASTHREAD
//#endif
#define DEBUGGER_STATEFILE_VERSION 0
#ifdef DEBUGGER_HASBLOCKFILE
#include "BlockFile.h"
#endif // DEBUGGER_HASBLOCKFILE
#ifdef DEBUGGER_HASLOGGER
#ifndef SUPERMODEL_VERSION
#define SUPERMODEL_VERSION ""
#endif // SUPERMODEL_VERSEION
#include "OSD/Logger.h"
#endif // DEBUGGER_HASLOGGER
#ifdef DEBUGGER_HASTHREAD
#include "OSD/Thread.h"
#endif // DEBUGGER_HASTHREAD
#ifndef stricmp
#ifdef _MSC_VER // MS VisualC++
#define stricmp _stricmp
#else // Assume GC
#define stricmp strcasecmp
#endif // _MSC_VER
#endif // stricmp
namespace Debugger
{
class CCPUDebug;
class CCodeAnalyser;
class CException;
class CInterrupt;
class CIO;
class CWatch;
class CBreakpoint;
class CRegMonitor;
enum EHaltReason
{
HaltNone = 0,
HaltState = 1,
HaltUser = 2,
HaltStep = 4,
HaltCount = 8,
HaltUntil = 16
};
enum EFormat
{
Hex = 0,
Hex0x,
HexDollar,
HexPostH,
Decimal,
Binary,
ASCII
};
/*
* Base class for a debugger.
* Provides common methods for loading/saving debugger state, formatting/parsing data and general control of the debugger.
* Also holds references to the CCPUDebug objects, one for each debuggable CPU, and contains pure virtual methods for sub-classes to
* implement which act as callbacks for the various debugging events that can occur.
*/
#ifdef DEBUGGER_HASLOGGER
class CDebugger : public CLogger
#else
class CDebugger
#endif // DEBUGGER_HASLOGGER
{
friend class CCPUDebug;
friend class CCodeAnalyser;
private:
bool m_exit;
bool m_pause;
bool m_break;
bool m_breakUser;
#ifdef DEBUGGER_HASTHREAD
CMutex *m_mutex;
CCPUDebug *m_primaryCPU;
#endif
protected:
virtual void AddCPUs() = 0;
virtual void DeleteCPUs();
#ifdef DEBUGGER_HASBLOCKFILE
virtual bool LoadState(CBlockFile *state);
virtual bool SaveState(CBlockFile *state);
#endif // DEBUGGER_HASBLOCKFILE
//
// Protected virtual methods for sub-classes to implement
//
virtual void AnalysisUpdated(CCodeAnalyser *analyser) = 0;
virtual void ExceptionTrapped(CException *ex) = 0;
virtual void InterruptTrapped(CInterrupt *in) = 0;
virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) = 0;
virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) = 0;
virtual void BreakpointReached(CBreakpoint *bp) = 0;
virtual void MonitorTriggered(CRegMonitor *regMon) = 0;
virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) = 0;
virtual void WaitCommand(CCPUDebug *cpu) = 0;
virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0;
public:
std::vector<CCPUDebug*> cpus;
UINT64 frameCount;
#ifdef DEBUGGER_HASLOGGER
bool logDebug;
bool logInfo;
bool logError;
#endif // DEBUGGER_HASLOGGER
static unsigned GetDataSize(UINT64 data);
static const char *GetSizeString(unsigned dataSize);
static UINT64 MaskData(unsigned dataSize, UINT64 data);
static UINT64 GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize);
static bool ParseInt(const char *str, int *val);
CDebugger();
virtual ~CDebugger();
void AddCPU(CCPUDebug *cpu);
void RemoveCPU(CCPUDebug *cpu);
CCPUDebug *GetCPU(const char *name);
void SetExit();
void SetPause(bool pause);
void ForceBreak(bool user);
void ClearBreak();
bool CheckExit();
bool CheckPause();
#ifdef DEBUGGER_HASTHREAD
bool MakePrimary(CCPUDebug *cpu);
void ReleasePrimary();
#endif // DEBUGGER_HASTHREAD
//
// Printing/logging
//
void PrintEvent(CCPUDebug *cpu, const char *fmt, ...);
#ifdef DEBUGGER_HASLOGGER
// CLogger logging methods
virtual void DebugLog(const char *fmt, va_list vl);
virtual void InfoLog(const char *fmt, va_list vl);
virtual void ErrorLog(const char *fmt, va_list vl);
#endif // DEBUGGER_HASLOGGER
//
// Formatting/parsing
//
// TODO - add in bufSize
virtual bool ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data);
virtual void FormatData(char *str, EFormat format, unsigned dataSize, INT64 data);
virtual void FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data);
//
// State loading/saving
//
virtual bool HasState();
virtual bool LoadState(const char *fileName);
virtual bool SaveState(const char *fileName);
//
// Public virtual methods for sub-classes to implement
//
virtual void Attach();
virtual void Detach();
virtual void Reset();
virtual void Poll();
};
//
// Inlined methods
//
inline bool CDebugger::CheckExit()
{
return m_exit;
}
inline bool CDebugger::CheckPause()
{
return m_pause;
}
}
#endif // INCLUDED_DEBUGGER_H
#endif // SUPERMODEL_DEBUGGER
+191
View File
@@ -0,0 +1,191 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* IO.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "CPUDebug.h"
#include "DebuggerIO.h"
#include "Watch.h"
namespace Debugger
{
CIO::CIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize) :
cpu(ioCPU), name(ioName), group(ioGroup), dataSize(ioDataSize),
watch(NULL), inCount(0), outCount(0), lastIn(0), lastOut(0), last(&lastIn)
{
//
}
bool CIO::CheckInput(UINT64 data)
{
inCount++;
RecordInput(data);
// Check watch, if any
return watch != NULL && watch->CheckInput(this, data);
}
bool CIO::CheckOutput(UINT64 data)
{
outCount++;
RecordOutput(data);
// Check watch, if any
return watch != NULL && watch->CheckOutput(this, data);
}
CSimpleWatch *CIO::AddSimpleWatch(bool trigInput, bool trigOutput)
{
CSimpleWatch *sWatch = new CSimpleWatch(cpu, this, trigInput, trigOutput);
cpu->AddWatch(sWatch);
return sWatch;
}
CCountWatch *CIO::AddCountWatch(bool trigInput, bool trigOutput, unsigned count)
{
CCountWatch *cWatch = new CCountWatch(cpu, this, trigInput, trigOutput, count);
cpu->AddWatch(cWatch);
return cWatch;
}
CMatchWatch *CIO::AddMatchWatch(bool trigInput, bool trigOutput, std::vector<UINT64> &dataSeq)
{
CMatchWatch *mWatch = new CMatchWatch(cpu, this, trigInput, trigOutput, dataSeq);
cpu->AddWatch(mWatch);
return mWatch;
}
CPrintWatch *CIO::AddPrintWatch(bool trigInput, bool trigOutput)
{
CPrintWatch *pWatch = new CPrintWatch(cpu, this, trigInput, trigOutput);
cpu->AddWatch(pWatch);
return pWatch;
}
CCaptureWatch *CIO::AddCaptureWatch(bool trigInput, bool trigOutput, unsigned maxLen)
{
CCaptureWatch *cWatch = new CCaptureWatch(cpu, this, trigInput, trigOutput, maxLen);
cpu->AddWatch(cWatch);
return cWatch;
}
bool CIO::RemoveWatch()
{
return watch != NULL && cpu->RemoveWatch(watch);
}
CPortIO::CPortIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT16 ioPortNum) :
CIO(ioCPU, ioName, ioGroup, ioDataSize), portNum(ioPortNum), label(NULL)
{
//
}
void CPortIO::AddLabel(const char *pLabel)
{
strncpy(labelStr, pLabel, MAX_LABEL_LENGTH);
labelStr[MAX_LABEL_LENGTH] = '\0';
label = labelStr;
}
void CPortIO::RemoveLabel()
{
label = NULL;
}
void CPortIO::GetLocation(char *str)
{
if (name != NULL)
strcpy(str, name);
else
sprintf(str, "port %u", portNum);
}
UINT64 CPortIO::Read()
{
UINT64 data = cpu->ReadPort(portNum);
RecordInput(data);
return data;
}
bool CPortIO::Write(UINT64 data)
{
if (!cpu->WritePort(portNum, data))
return false;
RecordOutput(data);
return true;
}
CMappedIO::CMappedIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT32 ioAddr) :
CIO(ioCPU, ioName, ioGroup, ioDataSize), CAddressRef(ioCPU, ioAddr, ioDataSize)
{
//
}
bool CMappedIO::CheckInput(UINT32 cAddr, unsigned cDataSize, UINT64 data)
{
// Take care with input that was not aligned with mapped I/O address and size
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, dataSize);
return CIO::CheckInput(sData);
}
bool CMappedIO::CheckOutput(UINT32 cAddr, unsigned cDataSize, UINT64 data)
{
// Take care with output that was not aligned with mapped I/O address and size
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, dataSize);
return CIO::CheckOutput(sData);
}
void CMappedIO::AddLabel(const char *label)
{
CIO::cpu->AddLabel(addr, label);
}
void CMappedIO::RemoveLabel()
{
CIO::cpu->RemoveLabel(addr);
}
void CMappedIO::GetLocation(char *str)
{
CIO::cpu->FormatAddress(str, addr);
}
UINT64 CMappedIO::Read()
{
UINT64 data = CIO::cpu->ReadMem(addr, dataSize);
RecordInput(data);
return data;
}
bool CMappedIO::Write(UINT64 data)
{
if (!CIO::cpu->WriteMem(addr, dataSize, data))
return false;
RecordOutput(data);
return true;
}
}
#endif // SUPERMODEL_DEBUGGER
+165
View File
@@ -0,0 +1,165 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* IO.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_IO_H
#define INCLUDED_IO_H
#include "Types.h"
#define MAX_LABEL_LENGTH 255
namespace Debugger
{
class CCPUDebug;
class CWatch;
class CSimpleWatch;
class CCountWatch;
class CMatchWatch;
class CPrintWatch;
class CCaptureWatch;
/*
* Base class that represents CPU input/output.
*/
class CIO
{
public:
CCPUDebug *cpu;
const char *name;
const char *group;
unsigned dataSize;
CWatch *watch;
unsigned inCount;
unsigned outCount;
UINT64 lastIn;
UINT64 lastOut;
UINT64 *last;
CIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize);
void RecordInput(UINT64 data);
void RecordOutput(UINT64 data);
bool CheckInput(UINT64 data);
bool CheckOutput(UINT64 data);
virtual void AddLabel(const char *name) = 0;
virtual void RemoveLabel() = 0;
CSimpleWatch *AddSimpleWatch(bool trigInput, bool trigOutput);
CCountWatch *AddCountWatch(bool trigInput, bool trigOutput, unsigned count);
CMatchWatch *AddMatchWatch(bool trigInput, bool trigOutput, std::vector<UINT64> &dataSeq);
CPrintWatch *AddPrintWatch(bool trigInput, bool trigOutput);
CCaptureWatch *AddCaptureWatch(bool trigInput, bool trigOutput, unsigned maxLen);
bool RemoveWatch();
virtual void GetLocation(char *str) = 0;
virtual UINT64 Read() = 0;
virtual bool Write(UINT64 data) = 0;
};
/*
* Class that represents a CPU input/ouput port.
*/
class CPortIO : public CIO
{
private:
char labelStr[MAX_LABEL_LENGTH + 1];
public:
const UINT16 portNum;
const char *label;
CPortIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT16 ioPortNum);
void AddLabel(const char *pLabel);
void RemoveLabel();
void GetLocation(char *str);
UINT64 Read();
bool Write(UINT64 data);
};
/*
* Class that represents CPU input/output that is mapped to a memory location.
*/
class CMappedIO : public CIO, public CAddressRef
{
public:
CMappedIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT32 ioAddr);
bool CheckInput(UINT32 cAddr, unsigned cDataSize, UINT64 data);
bool CheckOutput(UINT32 cAddr, unsigned cDataSize, UINT64 data);
void AddLabel(const char *name);
void RemoveLabel();
void GetLocation(char *str);
UINT64 Read();
bool Write(UINT64 data);
};
//
// Inlined methods
//
inline void CIO::RecordInput(UINT64 data)
{
// Keep track of data coming in
lastIn = data;
last = &lastIn;
}
inline void CIO::RecordOutput(UINT64 data)
{
// Keep track of data going out
lastOut = data;
last = &lastOut;
}
}
#endif // INCLUDED_IO_H
#endif // SUPERMODEL_DEBUGGER
+39
View File
@@ -0,0 +1,39 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Exception.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Exception.h"
namespace Debugger
{
CException::CException(CCPUDebug *exCPU, const char *exId, UINT16 exCode, const char *exName) :
cpu(exCPU), id(exId), code(exCode), name(exName), trap(false), count(0)
{
//
}
}
#endif // SUPERMODEL_DEBUGGER
+55
View File
@@ -0,0 +1,55 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Exception.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_EXCEPTION_H
#define INCLUDED_EXCEPTION_H
#include "Types.h"
namespace Debugger
{
class CCPUDebug;
/*
* Class that represents a CPU exception.
*/
class CException
{
public:
CCPUDebug *cpu;
const char *id;
const UINT16 code;
const char *name;
bool trap;
unsigned long count;
CException(CCPUDebug *exCPU, const char *exId, UINT16 exCode, const char *exName);
};
}
#endif // INCLUDED_EXCEPTION_H
#endif // SUPERMODEL_DEBUGGER
+39
View File
@@ -0,0 +1,39 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Interrupt.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Interrupt.h"
namespace Debugger
{
CInterrupt::CInterrupt(CCPUDebug *intCPU, const char *intId, UINT16 intCode, const char *intName) :
cpu(intCPU), id(intId), code(intCode), name(intName), trap(false), count(0)
{
//
}
}
#endif // SUPERMODEL_DEBUGGER
+55
View File
@@ -0,0 +1,55 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Interrupt.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_INTERRUPT_H
#define INCLUDED_INTERRUPT_H
#include "Types.h"
namespace Debugger
{
class CCPUDebug;
/*
* Class that represents a CPU interrupt (just a specialised CPU exception).
*/
class CInterrupt
{
public:
CCPUDebug *cpu;
const char *id;
const UINT16 code;
const char *name;
bool trap;
unsigned long count;
CInterrupt(CCPUDebug *intCPU, const char *intId, UINT16 intCode, const char *intName);
};
}
#endif // INCLUDED_INTERRUPT_H
#endif // SUPERMODEL_DEBUGGER
+71
View File
@@ -0,0 +1,71 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Label.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Label.h"
#include <cstring>
#include <string>
namespace Debugger
{
CLabel::CLabel(CCPUDebug *lCPU, UINT32 lAddr, const char *lName) : CAddressRef(lCPU, lAddr)
{
strncpy(nameStr, lName, MAX_LABEL_LENGTH);
nameStr[MAX_LABEL_LENGTH] = '\0';
name = nameStr;
}
CLabel::CLabel(CCPUDebug *lCPU, UINT32 lAddrStart, UINT32 lAddrEnd, const char *lName) :
CAddressRef(lCPU, lAddrStart, lAddrEnd - lAddrStart + 1)
{
strncpy(nameStr, lName, MAX_LABEL_LENGTH);
nameStr[MAX_LABEL_LENGTH] = '\0';
name = nameStr;
}
CComment::CComment(CCPUDebug *cCPU, UINT32 cAddr, const char *cText) : CAddressRef(cCPU, cAddr)
{
size_t size = strlen(cText);
char *textStr = new char[size + 1];
strncpy(textStr, cText, size);
textStr[size] = '\0';
text = textStr;
}
CComment::~CComment()
{
delete text;
}
CRegion::CRegion(CCPUDebug *rCPU, UINT32 rAddrStart, UINT32 rAddrEnd, bool rIsCode, bool rIsReadOnly, const char *rName) :
CLabel(rCPU, rAddrStart, rAddrEnd, rName), isCode(rIsCode), isReadOnly(rIsReadOnly), prevRegion(NULL), nextRegion(NULL)
{
//
}
}
#endif // SUPERMODEL_DEBUGGER
+85
View File
@@ -0,0 +1,85 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Label.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_LABEL_H
#define INCLUDED_LABEL_H
#include "AddressTable.h"
#include "Types.h"
#define MAX_LABEL_LENGTH 255
namespace Debugger
{
class CCPUDebug;
/*
* Class that represents a custom label added by user to a particular memory location.
*/
class CLabel : public CAddressRef
{
private:
char nameStr[MAX_LABEL_LENGTH + 1];
public:
const char *name;
CLabel(CCPUDebug *lCPU, UINT32 lAddr, const char *lName);
CLabel(CCPUDebug *lCPU, UINT32 lAddrStart, UINT32 lAddrEnd, const char *lName);
};
/*
* Class that stores a comment added by a user to a particular memory location.
*/
class CComment : public CAddressRef
{
public:
const char *text;
CComment(CCPUDebug *cCPU, UINT32 cAddr, const char *cText);
~CComment();
};
/*
* Class that represents various memory regions of a CPU, with information about the region.
*/
class CRegion : public CLabel
{
public:
const bool isCode;
const bool isReadOnly;
CRegion *prevRegion;
CRegion *nextRegion;
CRegion(CCPUDebug *rCPU, UINT32 rAddrStart, UINT32 rAddrEnd, bool rIsCode, bool rIsReadOnly, const char *rName);
};
}
#endif // INCLUDED_LABEL_H
#endif // SUPERMODEL_DEBUGGER
+396
View File
@@ -0,0 +1,396 @@
Help for Supermodel Console-based Debugger
==========================================
Relevant Supermodel Command Line Options
----------------------------------------
-enter-debugger Enters the debugger at the start of emulation,
-disable-debugger Completely disables the debugger in emulator.
At any point whilst running the emulator, execution can be halted and the debugger entered by pressing Alt+B.
Code Analysis
-------------
At startup, and whenever new code is encountered, the debugger analyses the program's code in order to work out all
reachable code locations from a given set of entry points (reset address, exception handlers etc). During the analysis,
it keeps track of all valid code locations and this is used to align instructions for CPUs with variable length instruction
sets. It also generates a set of auto-labels that identify places of interest such as sub-routines, jump destinations and
exception handlers etc. Code analysis can be switched off via the 'configure' command if required.
Debugger Commands
=================
Execution
---------
n next [<count>=1]
Runs the next single instruction or the next <count> instructions.
nf nextframe [<count>=1]
Runs until the next frame or for the next <count> frames.
s stepover
Runs the next instruction. If it is a call to a sub-routine then the sub-routine is 'stepped over' (ie control
breaks at the return address of the sub-routine).
si stepinto
Runs the next single instruction, entering any sub-routines (ie same as 'next' above).
so stepout
Runs until control returns from the current sub-routine or exception/interrupt handler. Note that if an
exception/interrupt occurs whilst stepping out then this will break execution too as control will have left
the sub-routine or handler.
c continue [<addr>]
Continues running until a break is forced or if <addr> specified, until the given address is reached.
spc setpc <addr>
Sets the PC of the current CPU to the given address.
CPUs
----
lc listcpus
Lists all the available CPUs with details about them.
sc switchcpu (<name>|<num>)
Switches to another CPU with the given name or number.
dc disablecpu (<name>|<num>)
Disables debugging for the CPU with the given name or number. Once disabled, it is no longer
possible to switch to or halt execution for that CPU, ie all breakpoints, watches, monitors and traps
are ignored. The current CPU cannot be disabled.
ec enablecpu (<name>|<num>)
Enables debugging for the CPU with the given name or number.
Registers
---------
lr listregisters
Lists all registers for the current CPU and their current values.
pr printregister <reg>
Prints the current value of the given register for the current CPU.
sr setregister <reg> <value>
Sets the value of the given register for the current CPU.
lm listmonitors
Lists all register monitors for the current CPU with details about them.
m monitor <reg>
am addmonitor <reg>
Adds a register monitor for the register with the given name, causing execution to break whenever the register's value
changes.
rm removemonitor <reg>
Removes the register monitor with the given name.
ram removeallmonitors
Removes all register monitors for the current CPU.
Exceptions & Interrupts
-----------------------
le listexceptions
Lists all known exceptions for the current CPU and details about them.
li listinterrupts
Lists all known interrupts for the current CPU and details about them.
t trap ((e)xception|(i)nterrupt) <id>
at addtrap ((e)xception|(i)nterrupt) <id>
Adds an exception or interrupt trap for the exception/interrupt with the given identifier.
rt removetrap ((e)xception|(i)nterrupt) <id>
Removes an exception or interrupt trap for the exception/interrupt with the given identifier.
rat removealltraps [(a)ll|(e)xceptions|(i)nterrupts]
Removes all exception and/or all interrupt traps for the current CPU. If no arguments are supplied, all exception and all
interrupt traps are removed.
Disassembly, Labels & Comments
------------------------------
l list [<start>=last [#<instrs>=20|<end>]]
ld listdisassembly [<start>=last [#<instrs>=20|<end>]]
Lists the disassembled code of the current CPU for the given address range and/or number of instructions.
If the start address is not supplied, the listing continues on from the last call, or from just before the current PC
address if this is the first call.
If the end address or instruction count is not supplied, then the default of 20 instructions are listed.
If code analysis is enabled, then this will be used to align the instructions in the disassembly. If code analysis is
off and the current PC address falls within the disassembly address range, then the instructions will line up with this.
Otherwise, instruction alignment will simply begin at the start address.
ll listlabels [(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints]
Lists all labels for the current CPU. The default option is to list all custom labels (user-added) and any auto-labels
(labels generated by code analysis) of interest. The other options allow the display of particular types of auto-labels.
al addlabel <addr> <name>
Adds a custom label at the given address and with the given name.
rl removelabel [<name>|<addr>]
Removes a custom label with the given name or at the given address. If no argument is supplied, then it removes the
custom label at the current PC address.
ral removealllabels
Removes all custom labels for the current CPU.
ac addcomment <addr> <text...>
Adds a code comment at the given address and with the given text.
rc removecomment [<addr>]
Removes the code comment at the given address. If no argument is supplied, then it removes the code comment at the
current PC address.
rac removeallcomments
Removes all code comments for the current CPU.
Breakpoints
-----------
lb listbreakpoints
Lists all breakpoints for the current CPU with details about them.
b breakpoint [<addr> [[s)imple|(c)ount <count>|(p)rint)]]
ab addbreakpoint [<addr> [[s)imple|(c)ount <count>|(p)rint)]]
Adds a breakpoint at the given address. If no arguments are supplied, then it adds a simple breakpoint at the current PC
address.
Types of breakpoint (default is simple):
(s)imple - if the location is hit, execution always breaks,
(c)ount - if the location is hit the number of times specified by count, execution breaks.
(p)rint - if the location is hit, a message is printed to the console, but control does not break.
rb removebreakpoint [#<num>|<addr>]
Removes the breakpoint with the given number of at the given address. If no arguments is supplied, then it removes
the breakpoint at the current PC address.
Memory, I/O & Watches
---------------------
ln listregions
Lists all known memory regions in the current CPU's address space.
ly listmemory [<start>=last [#<rows>=8|<end>]]
Lists the memory contents of the current CPU for the given address range and/or number of rows.
If the start address is not supplied, the listing continues on from the last call, or from address 0 if this is the
first call.
If the end address or row count is not supplied, then the default of 8 rows are listed.
py printmemory[.<size>=b] <addr> [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]
Prints the current memory value at the given address for the current CPU. If no format is supplied, the default data
format is used.
The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte.
sy setmemory[.<size>=b] <addr> <value>
Sets the memory value at the given address for the current CPU.
The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte.
lo listios
Lists all known I/Os (both mapped I/O addresses and I/O ports) for the current CPU and details about them.
lw listmemwatches
Lists all memory watches for the current CPU with details about them.
w memwatch[.<size>=b] <addr> [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount <count>|(m)atch <sequence>|captu(r)e <maxlen>|(p)rint)]]
aw addmemwatch[.<size>=b] <addr> [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount <count>|(m)atch <sequence>|captu(r)e <maxlen>|(p)rint)]]
Adds a read/write memory watch at the given address.
If (r)ead, (w)rite or (rw)eadwrite are specified then the watch also triggers an event (see below) when the
address is read, written or either read or written, respectively.
If (n)one is specified (the default), the watch does not trigger and simply remembers the last read/write at the
given address (details about which are visible when listing memory watches).
The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. If the
address is mapped I/O address, then the size is ignored.
Types of watch (default is simple):
(s)imple - if the watch is triggered, execution always breaks,
(c)ount - if the watch is triggered, execution breaks after the number of times specified by count,
(m)atch - if the watch is triggered, execution breaks if the series of values read/written matches the given
sequence of data (comma separated),
captu(r)e - if the watch is triggered, the value read/written is recorded in a history whose maximum length
is as specified,
(p)rint - if the watch is triggered, a message with the value read/written is printed to the console.
rw removememwatch (#<num>|<addr>)
Removes the memory watch with the given number or at the given address.
raw removeallmemwatches
Removes all memory watches for the current CPU.
lpw listportwatches
Lists all I/O port watches for the current CPU with details about them.
pw portwatch <port> [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount <count>|(m)atch <sequence>|captu(r)e <maxlen>|(p)rint]]
apw addportwatch <port> [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount <count>|(m)atch <sequence>|captu(r)e <maxlen>|(p)rint]]
Adds an input/output watch for the given I/O port.
If (i)nput, (o)utput or (io)nputoutput are specified then the watch also triggers an event (see below) when the
port is read (input), written (output) or either read or written, respectively.
If (n)one is specified (the default), the watch does not trigger and simply remembers the last read/write for the
given port (details about which are visible when listing port watches).
Types of watch (default is simple):
(s)imple - if the watch is triggered, execution always breaks,
(c)ount - if the watch is triggered the number of times specified by count, execution breaks,
(m)atch - if the watch is triggered, execution breaks if the series of values input/output matches the given
sequence of data (comma separated),
captu(r)e - if the watch is triggered, the value inputted/outputted is recorded in a history whose maximum length
is as specified,
(p)rint - if the watch is triggered, a message with the value inputted/outputted is printed to the console.
rpw removeportwatch (#<num>|<port>)
Removes the port watch with the given number or for the given port.
rapw removeallportwatches
Removes all port watches for the current CPU.
General
--------
p print[.<size>=v] <expr> [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]
Prints the given expression as a number in the given format. If no format is supplied, the default data format is used.
Currently the "expression" can just be a number, label or register name.
The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted depends on the
type of expression.
cfg configure analysis [(o)n|of(f)]
addrfmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]
portfmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]
datafmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]
showlabels [(o)n|of(f)]
showopcodes [(o)n|of(f)]
membytesrow [<num>]
Configures the debugger.
If no arguments are passed, it outputs all the current settings.
If a setting is supplied, it outputs the current value for the given setting.
If both a setting and a value are supplied, it sets the current value for the given setting.
ls loadstate <file>
Loads the debugger state (custom labels and code comments) from the given file.
ss savestate <file>
Save the debugger state (custom labels and code comments) to the given file.
h help
Prints the available commands.
x exit
Exits the debugger and emulator.
Emulator
--------
les loademustate <file>
Loads the emulator state from the given file.
ses saveemustate <file>
Saves the emulator state to the given file.
res resetemu
Resets the emulator.
Inputs
------
lip listinputs
Lists all available inputs for the current game.
pip printinput (<id>|<label>)
Prints details about the input with the given id or label.
sip setinput (<id>|<label>) <mapping>
Sets the current mapping for the input with the given id or label.
rip resetinput (<id>|<label>)
Resets the mapping to its default for the input with the given id or label.
cip configinput (<id>|<label>) [(s)et|(a)ppend]
Configures the input with the given id or label, in a similar fashion to -config-inputs. The default is to
set the mapping but if append is specified the mapping is appended to.
caip configallinputs
Configures all the inputs for the current game, in a similar fashion to -config-inputs.
Notes
-----
The output of any command can be redirected to a file by adding the > or >> redirection symbols after the command together
with the filename to write or append to, eg:
listdisassembly MainEntry #50 > c:\disassembly.txt
or listmemory $0000 $1000 >> ramdump.txt
+421
View File
@@ -0,0 +1,421 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Register.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "CPUDebug.h"
#include "Debugger.h"
#include "Register.h"
#include <cstdio>
#include <string>
namespace Debugger
{
CRegister::CRegister(CCPUDebug *regCPU, const char *regName, const char *regGroup, UINT8 regDataWidth) :
cpu(regCPU), name(regName), group(regGroup), dataWidth(regDataWidth)
{
//
}
double CRegister::GetValueAsFPoint()
{
return (double)GetValueAsInt();
}
CRegMonitor::CRegMonitor(CRegister *theReg) : reg(theReg)
{
//
}
CPCRegister::CPCRegMonitor::CPCRegMonitor(CPCRegister *theReg) : CRegMonitor(theReg)
{
Reset();
}
bool CPCRegister::CPCRegMonitor::HasChanged()
{
return m_prevPC != reg->cpu->pc;
}
void CPCRegister::CPCRegMonitor::GetBeforeValue(char *str)
{
reg->cpu->FormatAddress(str, m_prevPC);
}
void CPCRegister::CPCRegMonitor::Reset()
{
m_prevPC = reg->cpu->pc;
}
CPCRegister::CPCRegister(CCPUDebug *cpu, const char *name, const char *group) : CRegister(cpu, name, group, cpu->memBusWidth)
{
//
}
UINT32 CPCRegister::Get()
{
return cpu->pc;
}
bool CPCRegister::Set(UINT32 val)
{
return false;
}
void CPCRegister::GetValue(char *str)
{
if (cpu->instrCount > 0)
cpu->FormatAddress(str, cpu->pc);
else
{
str[0] = '-';
str[1] = '\0';
}
}
UINT64 CPCRegister::GetValueAsInt()
{
return cpu->pc;
}
bool CPCRegister::SetValue(const char *str)
{
return false;
}
CRegMonitor *CPCRegister::CreateMonitor()
{
return new CPCRegMonitor(this);
}
CAddrRegister::CAddrRegMonitor::CAddrRegMonitor(CAddrRegister *theReg) : CRegMonitor(theReg)
{
Reset();
}
bool CAddrRegister::CAddrRegMonitor::HasChanged()
{
return m_prevAddr != ((CAddrRegister*)reg)->Get();
}
void CAddrRegister::CAddrRegMonitor::GetBeforeValue(char *str)
{
reg->cpu->FormatAddress(str, m_prevAddr);
}
void CAddrRegister::CAddrRegMonitor::Reset()
{
m_prevAddr = ((CAddrRegister*)reg)->Get();
}
CAddrRegister::CAddrRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) :
CRegister(cpu, name, group, cpu->memBusWidth), m_id(id), m_getFunc(getFunc), m_setFunc(setFunc)
{
//
}
UINT32 CAddrRegister::Get()
{
return m_getFunc(cpu, m_id);
}
bool CAddrRegister::Set(UINT32 val)
{
return m_setFunc(cpu, m_id, val);
}
void CAddrRegister::GetValue(char *str)
{
cpu->FormatAddress(str, Get());
}
UINT64 CAddrRegister::GetValueAsInt()
{
return Get();
}
bool CAddrRegister::SetValue(const char *str)
{
UINT32 addr;
if (!cpu->ParseAddress(str, &addr))
return false;
return Set(addr);
}
CRegMonitor *CAddrRegister::CreateMonitor()
{
return new CAddrRegMonitor(this);
}
CIntRegister::CIntRegMonitor::CIntRegMonitor(CIntRegister *theReg) : CRegMonitor(theReg)
{
Reset();
}
bool CIntRegister::CIntRegMonitor::HasChanged()
{
return m_prevVal != ((CIntRegister*)reg)->Get();
}
void CIntRegister::CIntRegMonitor::GetBeforeValue(char *str)
{
reg->cpu->FormatData(str, reg->dataWidth / 8, m_prevVal);
}
void CIntRegister::CIntRegMonitor::Reset()
{
m_prevVal = ((CIntRegister*)reg)->Get();
}
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc) :
CRegister(cpu, name, group, 8), m_id(id),
m_get8Func(getFunc), m_set8Func(setFunc), m_get16Func(NULL), m_set16Func(NULL),
m_get32Func(NULL), m_set32Func(NULL), m_get64Func(NULL), m_set64Func(NULL)
{
//
}
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc) :
CRegister(cpu, name, group, 16), m_id(id),
m_get8Func(NULL), m_set8Func(NULL), m_get16Func(getFunc), m_set16Func(setFunc),
m_get32Func(NULL), m_set32Func(NULL), m_get64Func(NULL), m_set64Func(NULL)
{
//
}
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) :
CRegister(cpu, name, group, 32), m_id(id),
m_get8Func(NULL), m_set8Func(NULL), m_get16Func(NULL), m_set16Func(NULL),
m_get32Func(getFunc), m_set32Func(setFunc), m_get64Func(NULL), m_set64Func(NULL)
{
//
}
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc) :
CRegister(cpu, name, group, 64), m_id(id),
m_get8Func(NULL), m_set8Func(NULL), m_get16Func(NULL), m_set16Func(NULL),
m_get32Func(NULL), m_set32Func(NULL), m_get64Func(getFunc), m_set64Func(setFunc)
{
//
}
UINT64 CIntRegister::Get()
{
switch (dataWidth / 8)
{
case 1: return (UINT64)m_get8Func(cpu, m_id);
case 2: return (UINT64)m_get16Func(cpu, m_id);
case 4: return (UINT64)m_get32Func(cpu, m_id);
case 8: return m_get64Func(cpu, m_id);
default: return 0;
}
}
bool CIntRegister::Set(UINT64 val)
{
switch (dataWidth / 8)
{
case 1: return m_set8Func != NULL && m_set8Func(cpu, m_id, (UINT8)val);
case 2: return m_set16Func != NULL && m_set16Func(cpu, m_id, (UINT16)val);
case 4: return m_set32Func != NULL && m_set32Func(cpu, m_id, (UINT32)val);
case 8: return m_set64Func != NULL && m_set64Func(cpu, m_id, val);
default: return false;
}
}
void CIntRegister::GetValue(char *str)
{
cpu->FormatData(str, dataWidth / 8, Get());
}
UINT64 CIntRegister::GetValueAsInt()
{
return Get();
}
bool CIntRegister::SetValue(const char *str)
{
UINT64 data;
if (!cpu->ParseData(str, dataWidth / 8, &data))
return false;
return Set(data);
}
CRegMonitor *CIntRegister::CreateMonitor()
{
return new CIntRegMonitor(this);
}
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc) :
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
{
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
}
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc) :
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
{
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
}
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) :
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
{
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
}
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc) :
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
{
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
}
char &CStatusRegister::operator[](const unsigned index)
{
if (index >= dataWidth)
exit(1);
numBits = std::max<unsigned>(numBits, index);
return m_bitChrs[index];
}
void CStatusRegister::SetBits(const char *bitStr)
{
// Find end
const char *p = bitStr;
while (*p != '\0')
p++;
unsigned i = 0;
while (p-- > bitStr && i < dataWidth)
{
if (*p != offChr)
m_bitChrs[i] = *p;
i++;
}
numBits = i;
}
void CStatusRegister::GetValue(char *str)
{
UINT64 val = Get();
char *p = str + numBits;
*p-- = '\0';
for (unsigned i = 0; i < numBits; i++)
{
*p-- = (val&0x01 ? m_bitChrs[i] : offChr);
val >>= 1;
}
}
bool CStatusRegister::SetValue(const char *str)
{
UINT64 val = 0;
while (*str != '\0')
{
if (*str != offChr)
{
for (unsigned i = 0; i < numBits; i++)
{
if (m_bitChrs[i] == *str)
{
val |= 1LL<<i;
break;
}
}
}
str++;
}
return Set(val);
}
CFPointRegister::CFPointRegMonitor::CFPointRegMonitor(CFPointRegister *theReg) : CRegMonitor(theReg)
{
Reset();
}
bool CFPointRegister::CFPointRegMonitor::HasChanged()
{
return m_prevVal != ((CFPointRegister*)reg)->Get();
}
void CFPointRegister::CFPointRegMonitor::GetBeforeValue(char *str)
{
sprintf(str, "%f", m_prevVal);
}
void CFPointRegister::CFPointRegMonitor::Reset()
{
m_prevVal = ((CFPointRegister*)reg)->Get();
}
CFPointRegister::CFPointRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc) :
CRegister(cpu, name, group, 8), m_id(id), m_getFunc(getFunc), m_setFunc(setFunc)
{
//
}
double CFPointRegister::Get()
{
return m_getFunc(cpu, m_id);
}
bool CFPointRegister::Set(double val)
{
if (m_setFunc == NULL)
return false;
m_setFunc(cpu, m_id, val);
return true;
}
void CFPointRegister::GetValue(char *str)
{
sprintf(str, "%lf", Get());
}
UINT64 CFPointRegister::GetValueAsInt()
{
return (UINT64)Get();
}
double CFPointRegister::GetValueAsFPoint()
{
return Get();
}
bool CFPointRegister::SetValue(const char *str)
{
double val;
if (sscanf(str, "%lf", &val) != 1)
return false;
return Set(val);
}
CRegMonitor *CFPointRegister::CreateMonitor()
{
return new CFPointRegMonitor(this);
}
}
#endif // SUPERMODEL_DEBUGGER
+297
View File
@@ -0,0 +1,297 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Register.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_REGISTER_H
#define INCLUDED_REGISTER_H
#include "Types.h"
namespace Debugger
{
class CCPUDebug;
class CRegMonitor;
typedef UINT8 (*GetInt8FPtr)(CCPUDebug*, unsigned);
typedef bool (*SetInt8FPtr)(CCPUDebug*, unsigned, UINT8);
typedef UINT16 (*GetInt16FPtr)(CCPUDebug*, unsigned);
typedef bool (*SetInt16FPtr)(CCPUDebug*, unsigned, UINT16);
typedef UINT32 (*GetInt32FPtr)(CCPUDebug*, unsigned);
typedef bool (*SetInt32FPtr)(CCPUDebug*, unsigned, UINT32);
typedef UINT64 (*GetInt64FPtr)(CCPUDebug*, unsigned);
typedef bool (*SetInt64FPtr)(CCPUDebug*, unsigned, UINT64);
typedef double (*GetFPointFPtr)(CCPUDebug*, unsigned);
typedef bool (*SetFPointFPtr)(CCPUDebug*, unsigned, double);
/*
* Base class for a CPU register.
*/
class CRegister
{
protected:
CRegister(CCPUDebug *regCPU, const char *regName, const char *regGroup, UINT8 regDataWidth);
public:
CCPUDebug *cpu;
const char *name;
const char *group;
const UINT8 dataWidth;
virtual void GetValue(char *str) = 0;
virtual UINT64 GetValueAsInt() = 0;
virtual double GetValueAsFPoint();
virtual bool SetValue(const char *str) = 0;
virtual CRegMonitor *CreateMonitor() = 0;
};
/*
* Class that monitors a register to see if its value has changed.
*/
class CRegMonitor
{
protected:
CRegMonitor(CRegister *theReg);
public:
CRegister *reg;
virtual bool HasChanged() = 0;
virtual void GetBeforeValue(char *str) = 0;
virtual void Reset() = 0;
};
/*
* Class that represents the program counter of a CPU.
*/
class CPCRegister : public CRegister
{
private:
class CPCRegMonitor : public CRegMonitor
{
private:
UINT32 m_prevPC;
public:
CPCRegMonitor(CPCRegister *theReg);
bool HasChanged();
void GetBeforeValue(char *str);
void Reset();
};
public:
CPCRegister(CCPUDebug *cpu, const char *name, const char *group);
UINT32 Get();
bool Set(UINT32 val);
virtual void GetValue(char *str);
UINT64 GetValueAsInt();
virtual bool SetValue(const char *str);
CRegMonitor *CreateMonitor();
};
/*
* Class that represents a CPU address register.
* TODO - formatting as per CPCRegister?
*/
class CAddrRegister : public CRegister
{
private:
unsigned m_id;
GetInt32FPtr m_getFunc;
SetInt32FPtr m_setFunc;
class CAddrRegMonitor : public CRegMonitor
{
private:
UINT32 m_prevAddr;
public:
CAddrRegMonitor(CAddrRegister *theReg);
bool HasChanged();
void GetBeforeValue(char *str);
void Reset();
};
public:
CAddrRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);
UINT32 Get();
bool Set(UINT32 val);
virtual void GetValue(char *str);
UINT64 GetValueAsInt();
virtual bool SetValue(const char *str);
CRegMonitor *CreateMonitor();
};
/*
* Class that represents a general purpose integer CPU register.
*/
class CIntRegister : public CRegister
{
private:
unsigned m_id;
GetInt8FPtr m_get8Func;
SetInt8FPtr m_set8Func;
GetInt16FPtr m_get16Func;
SetInt16FPtr m_set16Func;
GetInt32FPtr m_get32Func;
SetInt32FPtr m_set32Func;
GetInt64FPtr m_get64Func;
SetInt64FPtr m_set64Func;
class CIntRegMonitor : public CRegMonitor
{
private:
UINT64 m_prevVal;
public:
CIntRegMonitor(CIntRegister *theReg);
bool HasChanged();
void GetBeforeValue(char *str);
void Reset();
};
public:
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc);
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc);
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc);
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc);
UINT64 Get();
bool Set(UINT64 val);
virtual void GetValue(char *str);
UINT64 GetValueAsInt();
virtual bool SetValue(const char *str);
CRegMonitor *CreateMonitor();
};
/*
* Class that represents a CPU status register that contains bit flags.
*/
class CStatusRegister : public CIntRegister
{
private:
char m_bitChrs[64];
public:
unsigned numBits;
char offChr;
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc);
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc);
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc);
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc);
char &operator[](const unsigned index);
void SetBits(const char *bitStr);
void GetValue(char *str);
bool SetValue(const char *str);
};
/*
* Class that represents a floating-point CPU register.
*/
class CFPointRegister : public CRegister
{
private:
unsigned m_id;
GetFPointFPtr m_getFunc;
SetFPointFPtr m_setFunc;
class CFPointRegMonitor : public CRegMonitor
{
private:
double m_prevVal;
public:
CFPointRegMonitor(CFPointRegister *theReg);
bool HasChanged();
void GetBeforeValue(char *str);
void Reset();
};
public:
CFPointRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc);
double Get();
bool Set(double val);
void GetValue(char *str);
UINT64 GetValueAsInt();
double GetValueAsFPoint();
bool SetValue(const char *str);
CRegMonitor *CreateMonitor();
};
}
#endif // INCLUDED_REGISTER_H
#endif // SUPERMODEL_DEBUGGER
+761
View File
@@ -0,0 +1,761 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* SupermodelDebugger.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Supermodel.h"
#include "ConsoleDebugger.h"
#include "CPUDebug.h"
#include "Label.h"
#include <cstdio>
#include <string>
namespace Debugger
{
static const char *s_giGroup = "Game Inputs";
static const char *s_gmGroup = "Game Inputs (Mirrored)";
static const char *s_sbGroup = "Sound Board";
static char s_mSlotStr[32][20];
static char s_sSlotStr[32][20];
CCPUDebug *CSupermodelDebugger::CreateMainBoardCPUDebug(::CModel3 *model3)
{
CPPCDebug *cpu = new CPPCDebug("MainPPC");
// Interrupts
cpu->AddInterrupt("VD0", 0, "Unknown video-related");
cpu->AddInterrupt("VBL", 1, "VBlank start");
cpu->AddInterrupt("VD2", 2, "Unknown video-related");
cpu->AddInterrupt("VD3", 3, "Unknown video-related");
cpu->AddInterrupt("NET", 4, "Network");
cpu->AddInterrupt("UN5", 5, "Unknown");
cpu->AddInterrupt("SND", 6, "SCSP (sound)");
cpu->AddInterrupt("UN7", 7, "Unknown");
// Memory regions
cpu->AddRegion(0x00000000, 0x007FFFFF, true, false, "RAM");
cpu->AddRegion(0x84000000, 0x8400003F, false, false, "Real3D Status Registers");
cpu->AddRegion(0x88000000, 0x88000007, false, false, "Real3D Command Port");
cpu->AddRegion(0x8C000000, 0x8C3FFFFF, false, false, "Real3D Culling RAM (Low)");
cpu->AddRegion(0x8E000000, 0x8E0FFFFF, false, false, "Real3D Culling RAM (High)");
cpu->AddRegion(0x90000000, 0x9000000B, false, false, "Real3D VROM Texture Port");
cpu->AddRegion(0x94000000, 0x940FFFFF, false, false, "Real3D Texture FIFO");
cpu->AddRegion(0x98000000, 0x980FFFFF, false, false, "Real3D Polygon RAM");
#ifndef NET_BOARD
cpu->AddRegion(0xC0000000, 0xC00000FF, false, false, "SCSI (Step 1.x)");
#endif
#ifdef NET_BOARD
cpu->AddRegion(0xC0000000, 0xC001FFFF, false, false, "Network Buffer");
cpu->AddRegion(0xC0020000, 0xC003FFFF, false, false, "Network RAM");
#endif
cpu->AddRegion(0xC1000000, 0xC10000FF, false, false, "SCSI (Step 1.x) (Lost World expects it here)");
cpu->AddRegion(0xC2000000, 0xC20000FF, false, false, "Real3D DMA (Step 2.x)");
cpu->AddRegion(0xF0040000, 0xF004003F, false, false, "Input (Controls) Registers");
cpu->AddRegion(0xF0080000, 0xF0080007, false, false, "Sound Board Registers");
cpu->AddRegion(0xF00C0000, 0xF00DFFFF, false, false, "Backup RAM");
cpu->AddRegion(0xF0100000, 0xF010003F, false, false, "System Registers");
cpu->AddRegion(0xF0140000, 0xF014003F, false, false, "Real, 0xTime Clock");
cpu->AddRegion(0xF0180000, 0xF019FFFF, false, false, "Security Board RAM");
cpu->AddRegion(0xF01A0000, 0xF01A003F, false, false, "Security Board Registers");
cpu->AddRegion(0xF0800CF8, 0xF0800CFF, false, false, "MPC105 CONFIG_cpu->AddR (Step 1.x)");
cpu->AddRegion(0xF0C00CF8, 0xF0C00CFF, false, false, "MPC105 CONFIG_DATA (Step 1.x)");
cpu->AddRegion(0xF1000000, 0xF10F7FFF, false, false, "Tile Generator Pattern Table");
cpu->AddRegion(0xF10F8000, 0xF10FFFFF, false, false, "Tile Generator Name Table");
cpu->AddRegion(0xF1100000, 0xF111FFFF, false, false, "Tile Generator Palette");
cpu->AddRegion(0xF1180000, 0xF11800FF, false, false, "Tile Generator Registers");
cpu->AddRegion(0xF8FFF000, 0xF8FFF0FF, false, false, "MPC105 (Step 1.x) or MPC106 (Step 2.x) Registers");
cpu->AddRegion(0xF9000000, 0xF90000FF, false, false, "NCR 53C810 Registers (Step 1.x?)");
cpu->AddRegion(0xFE040000, 0xFE04003F, false, false, "Mirrored Input Registers");
cpu->AddRegion(0xFEC00000, 0xFEDFFFFF, false, false, "MPC106 CONFIG_cpu->AddR (Step 2.x)");
cpu->AddRegion(0xFEE00000, 0xFEFFFFFF, false, false, "MPC106 CONFIG_DATA (Step 2.x)");
cpu->AddRegion(0xFF000000, 0xFF7FFFFF, true, true, "Banked CROM (CROMxx)");
cpu->AddRegion(0xFF800000, 0xFFFFFFFF, true, true, "Fixed CROM");
// Memory-mapped IO
cpu->AddMappedIO(0xF0040000, 1, "Input Bank Select", s_giGroup);
cpu->AddMappedIO(0xF0040004, 1, "Current Input Bank", s_giGroup);
cpu->AddMappedIO(0xF0040008, 1, "Game Specific Inputs 1", s_giGroup);
cpu->AddMappedIO(0xF004000C, 1, "Game Specific Inputs 2", s_giGroup);
cpu->AddMappedIO(0xF0040010, 1, "Drive Board", s_giGroup);
cpu->AddMappedIO(0xF0040014, 1, "LED Outputs?", s_giGroup);
cpu->AddMappedIO(0xF0040018, 1, "Unknown?", s_giGroup);
cpu->AddMappedIO(0xF0040024, 1, "Serial FIFO 1 Control", s_giGroup);
cpu->AddMappedIO(0xF0040028, 1, "Serial FIFO 2 Control", s_giGroup);
cpu->AddMappedIO(0xF004002C, 1, "Serial FIFO 1", s_giGroup);
cpu->AddMappedIO(0xF0040030, 1, "Serial FIFO 2", s_giGroup);
cpu->AddMappedIO(0xF0040034, 1, "Serial FIFO Flags", s_giGroup);
cpu->AddMappedIO(0xF004003C, 1, "ADC", s_giGroup);
cpu->AddMappedIO(0xFE040000, 1, "Input Bank Select", s_gmGroup);
cpu->AddMappedIO(0xFE040004, 1, "Current Input Bank", s_gmGroup);
cpu->AddMappedIO(0xFE040008, 1, "Game Specific Inputs 1", s_gmGroup);
cpu->AddMappedIO(0xFE04000C, 1, "Game Specific Inputs 2", s_gmGroup);
cpu->AddMappedIO(0xFE040010, 1, "Drive Board", s_gmGroup);
cpu->AddMappedIO(0xFE040014, 1, "LED Outputs?", s_gmGroup);
cpu->AddMappedIO(0xFE040018, 1, "Unknown?", s_gmGroup);
cpu->AddMappedIO(0xFE040024, 1, "Serial FIFO 1 Control", s_gmGroup);
cpu->AddMappedIO(0xFE040028, 1, "Serial FIFO 2 Control", s_gmGroup);
cpu->AddMappedIO(0xFE04002C, 1, "Serial FIFO 1", s_gmGroup);
cpu->AddMappedIO(0xFE040030, 1, "Serial FIFO 2", s_gmGroup);
cpu->AddMappedIO(0xFE040034, 1, "Serial FIFO Flags", s_gmGroup);
cpu->AddMappedIO(0xFE04003C, 1, "ADC", s_gmGroup);
cpu->AddMappedIO(0xF0080000, 1, "MIDI", s_sbGroup);
cpu->AddMappedIO(0xF0080004, 1, "Control", s_sbGroup);
return cpu;
}
CCPUDebug *CSupermodelDebugger::CreateSoundBoardCPUDebug(::CModel3 *model3)
{
CSoundBoard *sndBrd = model3->GetSoundBoard();
CMusashi68KDebug *cpu = new CMusashi68KDebug("Snd68K", sndBrd->GetM68K());
// Regions
cpu->AddRegion(0x000000, 0x0FFFFF, true, false, "SCSP1 RAM");
cpu->AddRegion(0x200000, 0x2FFFFF, true, false, "SCSP2 RAM");
cpu->AddRegion(0x600000, 0x67FFFF, true, true, "Program ROM");
cpu->AddRegion(0x800000, 0x9FFFFF, false, true, "Sample ROM (low 2 MB)");
cpu->AddRegion(0xA00000, 0xDFFFFF, false, true, "Sample ROM (bank)");
cpu->AddRegion(0xE00000, 0xFFFFFF, false, true, "Sample ROM (bank)");
cpu->AddRegion(0x100000, 0x10FFFF, false, false, "SCSP Master");
cpu->AddRegion(0x300000, 0x30FFFF, false, false, "SCSP Slave");
// Mapped I/O
// SCSP Master 32 slots
for (unsigned slot = 0; slot < 32; slot++)
{
sprintf(s_mSlotStr[slot], "SCSP Master Slot %02X", slot);
UINT32 addr = 0x100000 + slot * 0x20;
cpu->AddMappedIO(addr + 0x00, 2, "KYONX,KYONB,SBCTL,SSCTL,LPCTL,PCM8B,SA", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x02, 2, "SA", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x04, 2, "LSA", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x06, 2, "LEA", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x08, 2, "D2R,D1R,EGHOLD,AR", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0A, 2, "LPSLNK,KRS,DL,RR", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0C, 1, "STWINH", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0D, 1, "SDIR,TL", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0E, 2, "MDL,MDXSL,MDYSL", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x10, 2, "OCT,FNS", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x12, 1, "LFORE,LFOF,PLFOWS", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x13, 1, "PLFOS,ALFOWS,ALFOS", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x15, 1, "ISEL,OMXL", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x16, 1, "DISDL,DIPAN", s_mSlotStr[slot]);
cpu->AddMappedIO(addr + 0x17, 1, "EFSDL,EFPAN", s_mSlotStr[slot]);
}
// SCSP Master control registers
const char *masterCtl = "SCSP Master Control Registers";
cpu->AddMappedIO(0x100400, 1, "MEM4MB,DAC18B", masterCtl);
cpu->AddMappedIO(0x100401, 1, "VER,MVOL", masterCtl);
cpu->AddMappedIO(0x100402, 2, "RBL,RBP", masterCtl);
cpu->AddMappedIO(0x100404, 1, "MOFULL,MOEMP,MIOVF,MIFULL,MIEMP", masterCtl);
cpu->AddMappedIO(0x100405, 1, "MIBUF", masterCtl);
cpu->AddMappedIO(0x100407, 1, "MOBUF", masterCtl);
cpu->AddMappedIO(0x100408, 2, "MSLC,CA", masterCtl);
cpu->AddMappedIO(0x100412, 2, "DMEAL", masterCtl);
cpu->AddMappedIO(0x100414, 2, "DMEAH,DRGA", masterCtl);
cpu->AddMappedIO(0x100416, 2, "DGATE,DDIR,DEXE,DTLG", masterCtl);
cpu->AddMappedIO(0x100418, 1, "TACTL", masterCtl);
cpu->AddMappedIO(0x100419, 1, "TIMA", masterCtl);
cpu->AddMappedIO(0x10041A, 1, "TBCTL", masterCtl);
cpu->AddMappedIO(0x10041B, 1, "TIMB", masterCtl);
cpu->AddMappedIO(0x10041C, 1, "TCCTL", masterCtl);
cpu->AddMappedIO(0x10041D, 1, "TIMC", masterCtl);
cpu->AddMappedIO(0x10041E, 2, "SCIEB", masterCtl);
cpu->AddMappedIO(0x100420, 2, "SCIPD", masterCtl);
cpu->AddMappedIO(0x100422, 2, "SCIRE", masterCtl);
cpu->AddMappedIO(0x100425, 1, "SCILV0", masterCtl);
cpu->AddMappedIO(0x100427, 1, "SCILV1", masterCtl);
cpu->AddMappedIO(0x100429, 1, "SCILV2", masterCtl);
cpu->AddMappedIO(0x10042A, 2, "MCIEB", masterCtl);
cpu->AddMappedIO(0x10042C, 2, "MCIPD", masterCtl);
cpu->AddMappedIO(0x10042E, 2, "MCIRE", masterCtl);
// SCSP Slave 32 slots
for (unsigned slot = 0; slot < 32; slot++)
{
sprintf(s_sSlotStr[slot], "SCSP Slave Slot %02X", slot);
UINT32 addr = 0x300000 + slot * 0x20;
cpu->AddMappedIO(addr + 0x00, 2, "KYONX,KYONB,SBCTL,SSCTL,LPCTL,PCM8B,SA", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x02, 2, "SA", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x04, 2, "LSA", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x06, 2, "LEA", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x08, 2, "D2R,D1R,EGHOLD,AR", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0A, 2, "LPSLNK,KRS,DL,RR", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0C, 1, "STWINH", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0D, 1, "SDIR,TL", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x0E, 2, "MDL,MDXSL,MDYSL", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x10, 2, "OCT,FNS", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x12, 1, "LFORE,LFOF,PLFOWS", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x13, 1, "PLFOS,ALFOWS,ALFOS", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x15, 1, "ISEL,OMXL", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x16, 1, "DISDL,DIPAN", s_sSlotStr[slot]);
cpu->AddMappedIO(addr + 0x17, 1, "EFSDL,EFPAN", s_sSlotStr[slot]);
}
// SCSP Master control registers
const char *slaveCtl = "SCSP Slave Control Registers";
cpu->AddMappedIO(0x300400, 1, "MEM4MB,DAC18B", slaveCtl);
cpu->AddMappedIO(0x300401, 1, "VER,MVOL", slaveCtl);
cpu->AddMappedIO(0x300402, 2, "RBL,RBP", slaveCtl);
cpu->AddMappedIO(0x300404, 1, "MOFULL,MOEMP,MIOVF,MIFULL,MIEMP", slaveCtl);
cpu->AddMappedIO(0x300405, 1, "MIBUF", slaveCtl);
cpu->AddMappedIO(0x300407, 1, "MOBUF", slaveCtl);
cpu->AddMappedIO(0x300408, 2, "MSLC,CA", slaveCtl);
cpu->AddMappedIO(0x300412, 2, "DMEAL", slaveCtl);
cpu->AddMappedIO(0x300414, 2, "DMEAH,DRGA", slaveCtl);
cpu->AddMappedIO(0x300416, 2, "DGATE,DDIR,DEXE,DTLG", slaveCtl);
cpu->AddMappedIO(0x300418, 1, "TACTL", slaveCtl);
cpu->AddMappedIO(0x300419, 1, "TIMA", slaveCtl);
cpu->AddMappedIO(0x30041A, 1, "TBCTL", slaveCtl);
cpu->AddMappedIO(0x30041B, 1, "TIMB", slaveCtl);
cpu->AddMappedIO(0x30041C, 1, "TCCTL", slaveCtl);
cpu->AddMappedIO(0x30041D, 1, "TIMC", slaveCtl);
cpu->AddMappedIO(0x30041E, 2, "SCIEB", slaveCtl);
cpu->AddMappedIO(0x300420, 2, "SCIPD", slaveCtl);
cpu->AddMappedIO(0x300422, 2, "SCIRE", slaveCtl);
cpu->AddMappedIO(0x300425, 1, "SCILV0", slaveCtl);
cpu->AddMappedIO(0x300427, 1, "SCILV1", slaveCtl);
cpu->AddMappedIO(0x300429, 1, "SCILV2", slaveCtl);
cpu->AddMappedIO(0x30042A, 2, "MCIEB", slaveCtl);
cpu->AddMappedIO(0x30042C, 2, "MCIPD", slaveCtl);
cpu->AddMappedIO(0x30042E, 2, "MCIRE", slaveCtl);
return cpu;
}
CCPUDebug *CSupermodelDebugger::CreateDSBCPUDebug(::CModel3 *model3)
{
CSoundBoard *sndBrd = model3->GetSoundBoard();
CDSB *dsb = sndBrd->GetDSB();
CDSB1 *dsb1 = dynamic_cast<CDSB1*>(dsb);
if (dsb1 != NULL)
{
CZ80Debug *cpu = new CZ80Debug("DSBZ80", dsb1->GetZ80());
// Regions
cpu->AddRegion(0x0000, 0x7FFF, true, true, "ROM");
cpu->AddRegion(0x8000, 0xFFFF, false, false, "RAM");
// TODO - rename some I/O ports
return cpu;
}
CDSB2 *dsb2 = dynamic_cast<CDSB2*>(dsb);
if (dsb2 != NULL)
{
CMusashi68KDebug *cpu = new CMusashi68KDebug("DSB68K", dsb2->GetM68K());
// Regions
cpu->AddRegion(0x000000, 0x020000, true, true, "ROM");
cpu->AddRegion(0xF00000, 0xF10000, false, false, "RAM");
// TODO - memory mapped I/O
return cpu;
}
return NULL;
}
CCPUDebug *CSupermodelDebugger::CreateDriveBoardCPUDebug(::CModel3 *model3)
{
CDriveBoard *drvBrd = model3->GetDriveBoard();
if (!drvBrd->IsAttached())
return NULL;
CZ80Debug *cpu = new CZ80Debug("DrvZ80", drvBrd->GetZ80());
// Regions
cpu->AddRegion(0x0000, 0x7FFF, true, true, "ROM");
cpu->AddRegion(0xE000, 0xFFFF, false, false, "RAM");
// TODO - rename some I/O ports
return cpu;
}
#ifdef NET_BOARD
CCPUDebug *CSupermodelDebugger::CreateNetBoardCPUDebug(::CModel3 * model3)
{
CNetBoard *netBrd = model3->GetNetBoard();
if (!netBrd->IsAttached())
return NULL;
CMusashi68KDebug *cpu = new CMusashi68KDebug("NET68K", netBrd->GetM68K());
// Regions
cpu->AddRegion(0x000000, 0x01ffff, false, false, "Net buffer");
cpu->AddRegion(0x020000, 0x03ffff, true, false, "Net RAM");
cpu->AddRegion(0x040000, 0x0401ff, false, false, "Net 1"); // ??? size unknown
cpu->AddRegion(0x080000, 0x0bffff, false, false, "Net 2"); // commram ???
cpu->AddRegion(0x0c0000, 0x0c01ff, false, false, "Net 3"); // ??? size unknown
const char *NetReg = "NetBoard Control Registers";
cpu->AddMappedIO(0x010110, 4, "Reg 1", NetReg);
cpu->AddMappedIO(0x010114, 4, "Reg 2", NetReg);
cpu->AddMappedIO(0x010180, 4, "Reg 3", NetReg);
return cpu;
}
#endif
CSupermodelDebugger::CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger) :
CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger),
m_loadEmuState(false), m_saveEmuState(false), m_resetEmu(false)
{
//
}
void CSupermodelDebugger::AddCPUs()
{
CCPUDebug *cpu;
// Add main board CPU
cpu = CreateMainBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
// Add sound board CPU (if attached)
cpu = CreateSoundBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
// Add sound daughter board CPU (if attached)
cpu = CreateDSBCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
// Add drive board CPU (if attached)
cpu = CreateDriveBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
#ifdef NET_BOARD
// Add net board CPU (if attached)
cpu = CreateNetBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
#endif
}
void CSupermodelDebugger::WaitCommand(CCPUDebug *cpu)
{
// Ungrab mouse and disable audio
m_inputs->GetInputSystem()->UngrabMouse();
SetAudioEnabled(false);
CConsoleDebugger::WaitCommand(cpu);
m_inputs->GetInputSystem()->GrabMouse();
SetAudioEnabled(true);
}
bool CSupermodelDebugger::ProcessToken(const char *token, const char *cmd)
{
//
// Emulator
//
if (CheckToken(token, "les", "loademustate")) // loademustate <filename>
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Missing filename.\n");
return false;
}
strncpy(m_stateFile, token, 254);
m_stateFile[254] = '\0';
m_loadEmuState = true;
return true;
}
else if (CheckToken(token, "ses", "saveemustate")) // saveemustate <filename>
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Missing filename.\n");
return false;
}
strncpy(m_stateFile, token, 254);
m_stateFile[254] = '\0';
m_saveEmuState = true;
return true;
}
else if (CheckToken(token, "res", "resetemu")) // resetemu
{
m_resetEmu = true;
return true;
}
//
// Inputs
//
else if (CheckToken(token, "lip", "listinputs")) // listinputs
{
ListInputs();
return false;
}
else if (CheckToken(token, "pip", "printinput")) // printinput (<id>|<label>)
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input name.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
if (!input->IsVirtual())
{
char mapTrunc[41];
Truncate(mapTrunc, 40, input->GetMapping());
Print("Input %s (%s) [%s] = %04X (%d)\n", input->id, input->label, mapTrunc, input->value, input->value);
}
else
Print("Input %s (%s) = %04X (%d)\n", input->id, input->label, input->value, input->value);
return false;
}
else if (CheckToken(token, "sip", "setinput")) // setinput (<id>|<label>) <mapping>
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input id or label.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Missing mapping to set.\n");
return false;
}
input->SetMapping(token);
Print("Set input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
return false;
}
else if (CheckToken(token, "rip", "resetinput")) // resetinput (<id>|<label>)
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input id or label.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
input->ResetToDefaultMapping();
Print("Reset input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
return false;
}
else if (CheckToken(token, "cip", "configinput")) // configinput (<id>|<label>) [(s)et|(a)ppend]
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input id or label.\n");
return false;
}
CInput *input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
token = strtok(NULL, " ");
bool append;
if (token == NULL || CheckToken(token, "s", "set"))
append = false;
else if (CheckToken(token, "a", "append"))
append = true;
else
{
Print("Enter a valid mode (s)et or (a)ppend.\n");
return false;
}
Print("Configure input %s [%s]: %s...", input->label, input->GetMapping(), (append ? "Appending" : "Setting"));
fflush(stdout); // required on terminals that use buffering
// Configure the input
if (input->Configure(append, "KEY_ESCAPE"))
Print(" %s\n", input->GetMapping());
else
Print(" [Cancelled]\n");
return false;
}
else if (CheckToken(token, "caip", "configallinputs")) // configallinputs
{
m_inputs->ConfigureInputs(&m_model3->GetGame());
return false;
}
//
// Help
//
else if (CheckToken(token, "h", "help"))
{
CConsoleDebugger::ProcessToken(token, cmd);
const char *fmt = " %-6s %-25s %s\n";
Print(" Emulator:\n");
Print(fmt, "les", "loademustate", "<filename>");
Print(fmt, "ses", "saveemustate", "<filename>");
Print(fmt, "res", "resetemu", "");
Print(" Inputs:\n");
Print(fmt, "lip", "listinputs", "");
Print(fmt, "pip", "printinput", "(<id>|<label>)");
Print(fmt, "sip", "setinput", "(<id>|<label>) <mapping>");
Print(fmt, "rip", "resetinput", "(<id>|<label>)");
Print(fmt, "cip", "configinput", "(<id>|<label>) [(s)et|(a)ppend]");
Print(fmt, "caip", "configallinputs", "");
return false;
}
else
return CConsoleDebugger::ProcessToken(token, cmd);
}
bool CSupermodelDebugger::InputIsValid(::CInput *input)
{
return input->IsUIInput() || (input->gameFlags & m_model3->GetGame().inputs);
}
void CSupermodelDebugger::ListInputs()
{
Print("Inputs:\n");
if (m_inputs->Count() == 0)
Print(" None\n");
// Get maximum id, label and mapping widths
size_t idAndLabelWidth = 0;
size_t mappingWidth = 0;
for (unsigned i = 0; i < m_inputs->Count(); i++)
{
::CInput *input = (*m_inputs)[i];
if (!InputIsValid(input))
continue;
idAndLabelWidth = std::max<size_t>(idAndLabelWidth, strlen(input->id) + strlen(input->label) + 3);
if (!input->IsVirtual())
mappingWidth = std::max<size_t>(mappingWidth, strlen(input->GetMapping()));
}
mappingWidth = std::min<size_t>(mappingWidth, 20);
// Print labels, mappings and values for each input
const char *groupLabel = NULL;
char idAndLabel[255];
char mapping[21];
for (unsigned i = 0; i < m_inputs->Count(); i++)
{
::CInput *input = (*m_inputs)[i];
if (!InputIsValid(input))
continue;
if (groupLabel == NULL || stricmp(groupLabel, input->GetInputGroup()) != 0)
{
groupLabel = input->GetInputGroup();
Print(" %s:\n", groupLabel);
}
sprintf(idAndLabel, "%s (%s)", input->id, input->label);
Print(" %-*s", (int)idAndLabelWidth, idAndLabel);
if (!input->IsVirtual())
Truncate(mapping, 20, input->GetMapping());
else
mapping[0] = '\0';
Print(" %-*s %04X (%d)\n", (int)mappingWidth, mapping, input->value, input->value);
}
}
void CSupermodelDebugger::Attached()
{
CConsoleDebugger::Attached();
char fileName[25];
sprintf(fileName, "Debug/%s.ds", m_model3->GetGame().name.c_str());
LoadState(fileName);
}
void CSupermodelDebugger::Detaching()
{
char fileName[25];
sprintf(fileName, "Debug/%s.ds", m_model3->GetGame().name.c_str());
SaveState(fileName);
CConsoleDebugger::Detaching();
}
bool CSupermodelDebugger::LoadModel3State(const char *fileName)
{
// Open file and find header
CBlockFile state;
if (state.Load(fileName) != OKAY)
return false;
if (state.FindBlock("Debugger Model3 State") != OKAY)
{
state.Close();
return false;
}
// Check version in header matches
unsigned version;
state.Read(&version, sizeof(version));
if (version != MODEL3_STATEFILE_VERSION)
{
state.Close();
return false;
}
// Load Model3 state
m_model3->LoadState(&state);
state.Close();
// Reset debugger
Reset();
return true;
}
bool CSupermodelDebugger::SaveModel3State(const char *fileName)
{
// Create file with header
CBlockFile state;
if (state.Create(fileName, "Debugger Model3 State", __FILE__) != OKAY)
return false;
// Write out version in header
unsigned version = MODEL3_STATEFILE_VERSION;
state.Write(&version, sizeof(version));
// Save Model3 state
m_model3->SaveState(&state);
state.Close();
return true;
}
void CSupermodelDebugger::ResetModel3()
{
// Reset Model3
m_model3->Reset();
// Reset debugger
Reset();
}
void CSupermodelDebugger::DebugLog(const char *fmt, va_list vl)
{
// Use the supplied logger, if any
if (m_logger != NULL)
m_logger->DebugLog(fmt, vl);
else
CConsoleDebugger::DebugLog(fmt, vl);
}
void CSupermodelDebugger::InfoLog(const char *fmt, va_list vl)
{
// Use the supplied logger, if any
if (m_logger != NULL)
m_logger->InfoLog(fmt, vl);
else
CConsoleDebugger::InfoLog(fmt, vl);
}
void CSupermodelDebugger::ErrorLog(const char *fmt, va_list vl)
{
// Use the supplied logger, if any
if (m_logger != NULL)
m_logger->ErrorLog(fmt, vl);
else
CConsoleDebugger::ErrorLog(fmt, vl);
}
void CSupermodelDebugger::Poll()
{
CConsoleDebugger::Poll();
// Load/saving of emulator state and resetting emulator must be done here
if (m_loadEmuState)
{
LoadModel3State(m_stateFile);
m_loadEmuState = false;
ForceBreak(false);
}
else if (m_saveEmuState)
{
SaveModel3State(m_stateFile);
m_saveEmuState = false;
ForceBreak(false);
}
else if (m_resetEmu)
{
ResetModel3();
m_resetEmu = false;
ForceBreak(false);
}
}
}
#endif // SUPERMODEL_DEBUGGER
+105
View File
@@ -0,0 +1,105 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* SupermodelDebugger.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_SUPERMODELDEBUGGER_H
#define INCLUDED_SUPERMODELDEBUGGER_H
#include "ConsoleDebugger.h"
#include <stdarg.h>
#define MODEL3_STATEFILE_VERSION 0
class CModel3;
class CInputs;
class CInput;
namespace Debugger
{
class CCPUDebug;
/*
* Console-based debugger used by Supermodel.
*/
class CSupermodelDebugger : public CConsoleDebugger
{
private:
::CModel3 *m_model3;
::CInputs *m_inputs;
::CLogger *m_logger;
bool m_loadEmuState;
bool m_saveEmuState;
bool m_resetEmu;
char m_stateFile[255];
bool InputIsValid(CInput *input);
void ListInputs();
protected:
void AddCPUs();
void WaitCommand(CCPUDebug *cpu);
bool ProcessToken(const char *token, const char *cmd);
void Attached();
void Detaching();
public:
static CCPUDebug *CreateMainBoardCPUDebug(::CModel3 *model3);
static CCPUDebug *CreateSoundBoardCPUDebug(::CModel3 *model3);
static CCPUDebug *CreateDSBCPUDebug(::CModel3 *model3);
static CCPUDebug *CreateDriveBoardCPUDebug(::CModel3 *model3);
#ifdef NET_BOARD
static CCPUDebug *CreateNetBoardCPUDebug(::CModel3 *model3);
#endif
CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger);
void Poll();
bool LoadModel3State(const char *fileName);
bool SaveModel3State(const char *fileName);
void ResetModel3();
void DebugLog(const char *fmt, va_list vl);
void InfoLog(const char *fmt, va_list vl);
void ErrorLog(const char *fmt, va_list vl);
};
}
#endif // INCLUDED_SUPERMODELDEBUGGER_H
#endif // SUPERMODEL_DEBUGGER
+307
View File
@@ -0,0 +1,307 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Watch.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "CPUDebug.h"
#include "Debugger.h"
#include "DebuggerIO.h"
#include "Watch.h"
#include <string>
using namespace std;
namespace Debugger
{
UINT32 CWatch::GetIOAddress(CIO *io)
{
CMappedIO *mappedIO = dynamic_cast<CMappedIO*>(io);
return (mappedIO != NULL ? mappedIO->addr : 0);
}
CWatch::CWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, char wSymbol, const char *wType, bool wTrigRead, bool wTrigWrite) :
CAddressRef(wCPU, wAddr, wSize), io(NULL), symbol(wSymbol), type(wType), trigRead(wTrigRead), trigWrite(wTrigWrite), active(true),
readCount(0), writeCount(0)
{
//
}
CWatch::CWatch(CCPUDebug *wCPU, CIO *wIO, char wSymbol, const char *wType, bool wTrigInput, bool mTrigOutput) :
CAddressRef(wCPU, GetIOAddress(wIO), wIO->dataSize), io(wIO), symbol(wSymbol), type(wType), trigRead(wTrigInput), trigWrite(mTrigOutput), active(true),
readCount(0), writeCount(0)
{
//
}
bool CWatch::HasValue()
{
if (io != NULL)
return io->inCount + io->outCount > 0;
else
return true;
}
UINT64 CWatch::GetValue()
{
if (io != NULL)
return *io->last;
else
return cpu->ReadMem(addr, size);
}
bool CWatch::CheckRead(UINT32 cAddr, unsigned cDataSize, UINT64 data)
{
if (!active || !trigRead)
return false;
// Take care with read that was not aligned with watch address and size
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, size);
return CheckBreak(true, sData);
}
bool CWatch::CheckWrite(UINT32 cAddr, unsigned cDataSize, UINT64 data)
{
if (!active || !trigWrite)
return false;
// Take care with write that was not aligned with watch address and size
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, size);
return CheckBreak(false, sData);
}
bool CWatch::CheckInput(CIO *cIO, UINT64 data)
{
// No need to align data as already aligned by CIO
return active && trigRead && CheckBreak(true, data);
}
bool CWatch::CheckOutput(CIO *cIO, UINT64 data)
{
// No need to align data as already aligned by CIO
return active && trigWrite && CheckBreak(false, data);
}
void CWatch::Reset()
{
//
}
bool CWatch::GetInfo(char *str)
{
return false;
}
CSimpleWatch::CSimpleWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite) :
CWatch(wCPU, wAddr, wSize, '*', "simple", wTrigRead, wTrigWrite)
{
//
}
CSimpleWatch::CSimpleWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput) :
CWatch(wCPU, wIO, '*', "simple", wTrigInput, mTrigOutput)
{
//
}
bool CSimpleWatch::CheckBreak(bool isRead, UINT64 data)
{
return true;
}
CCountWatch::CCountWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned count) :
CWatch(wCPU, wAddr, wSize, '+', "count", wTrigRead, wTrigWrite), m_count(count), m_counter(0)
{
//
}
CCountWatch::CCountWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput, unsigned count) :
CWatch(wCPU, wIO, '+', "count", wTrigInput, mTrigOutput), m_count(count), m_counter(0)
{
//
}
bool CCountWatch::CheckBreak(bool isRead, UINT64 data)
{
return ++m_counter == m_count;
}
void CCountWatch::Reset()
{
m_counter = 0;
}
bool CCountWatch::GetInfo(char *str)
{
sprintf(str, "%d / %d", m_counter, m_count);
return true;
}
CMatchWatch::CMatchWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, vector<UINT64> &dataSeq) :
CWatch(wCPU, wAddr, wSize, 'M', "match", wTrigRead, wTrigWrite), m_dataLen(dataSeq.size()), m_counter(0)
{
m_dataArray = new UINT64[m_dataLen];
copy(dataSeq.begin(), dataSeq.end(), m_dataArray);
}
CMatchWatch::CMatchWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput, vector<UINT64> &dataSeq) :
CWatch(wCPU, wIO, 'M', "match", wTrigInput, mTrigOutput), m_dataLen(dataSeq.size()), m_counter(0)
{
m_dataArray = new UINT64[m_dataLen];
copy(dataSeq.begin(), dataSeq.end(), m_dataArray);
}
CMatchWatch::~CMatchWatch()
{
delete m_dataArray;
}
bool CMatchWatch::CheckBreak(bool isRead, UINT64 data)
{
if (m_dataArray[m_counter] == data)
m_counter++;
else
m_counter = 0;
return m_counter == m_dataLen;
}
void CMatchWatch::Reset()
{
m_counter = 0;
}
bool CMatchWatch::GetInfo(char *str)
{
sprintf(str, "%d / %d matched", m_counter, m_dataLen);
return true;
}
CCaptureWatch::CCaptureWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned wMaxLen) :
CWatch(wCPU, wAddr, wSize, 'C', "capture", wTrigRead, wTrigWrite), m_maxLen(min<unsigned>(255, wMaxLen)), m_len(0), m_index(0)
{
//
}
CCaptureWatch::CCaptureWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput, unsigned wMaxLen) :
CWatch(wCPU, wIO, 'C', "capture", wTrigInput, mTrigOutput), m_maxLen(min<unsigned>(255, wMaxLen)), m_len(0), m_index(0)
{
//
}
bool CCaptureWatch::CheckBreak(bool isRead, UINT64 data)
{
if (m_len < m_maxLen)
{
m_dataVals[m_len] = data;
m_dataRW[m_len] = isRead;
m_len++;
m_index = 0;
}
else
{
m_dataVals[m_index] = data;
m_dataRW[m_index] = isRead;
m_index++;
if (m_index >= m_maxLen)
m_index = 0;
}
return false;
}
void CCaptureWatch::Reset()
{
m_len = 0;
m_index = 0;
}
bool CCaptureWatch::GetInfo(char *str)
{
char dataStr[25];
unsigned ind, len;
str[0] = '\0';
if (m_len == 0)
return true;
else if (m_len < m_maxLen)
{
ind = m_len - 1;
len = m_len;
}
else
{
if (m_index == 0)
ind = m_maxLen - 1;
else
ind = m_index - 1;
len = m_maxLen;
}
for (unsigned i = 0; i < len; i++)
{
UINT64 data = m_dataVals[ind];
bool isRead = m_dataRW[ind];
cpu->FormatData(dataStr, size, data);
if (i > 0)
strcat(str, " ");
strcat(str, (isRead ? "<" : ">"));
strcat(str, dataStr);
if (ind == 0)
ind = m_maxLen - 1;
else
ind--;
}
return true;
}
CPrintWatch::CPrintWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite) :
CWatch(wCPU, wAddr, wSize, 'P', "print", wTrigRead, wTrigWrite)
{
//
}
CPrintWatch::CPrintWatch(CCPUDebug *wCPU, CIO *io, bool wTrigInput, bool mTrigOutput) :
CWatch(wCPU, io, 'P', "print", wTrigInput, mTrigOutput)
{
//
}
bool CPrintWatch::CheckBreak(bool isRead, UINT64 data)
{
const char *sizeStr = CDebugger::GetSizeString(size);
const char *rwStr = (isRead ? "Read" : "Wrote");
const char *tfStr = (isRead ? "from" : "to");
char dataStr[50];
char addrStr[50];
char locStr[50];
cpu->FormatData(dataStr, size, data);
cpu->FormatAddress(addrStr, addr, true);
if (io != NULL)
{
io->GetLocation(locStr);
cpu->debugger->PrintEvent(cpu, "%s %s data (%s) %s I/O %s.\n", rwStr, sizeStr, dataStr, tfStr, locStr);
}
else
cpu->debugger->PrintEvent(cpu, "%s %s data (%s) %s memory %s.\n", rwStr, sizeStr, dataStr, tfStr, addrStr);
return false;
}
}
#endif // SUPERMODEL_DEBUGGER
+182
View File
@@ -0,0 +1,182 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Watch.h
*/
#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_WATCH_H
#define INCLUDED_WATCH_H
#include "AddressTable.h"
#include "Types.h"
#include <vector>
namespace Debugger
{
class CCPUDebug;
class CIOPort;
/*
* Base class for a memory/IO watch. All watches keep track of the last read/write to the memory location or I/O port.
*/
class CWatch : public CAddressRef
{
private:
static UINT32 GetIOAddress(CIO *io);
protected:
CWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, char wSymbol, const char *wType, bool wTrigRead, bool wTrigWrite);
CWatch(CCPUDebug *wCPU, CIO *wIO, char wSymbol, const char *wType, bool wTrigInput, bool wTrigOutput);
public:
CIO *io;
const char symbol;
const char *type;
bool trigRead;
bool trigWrite;
unsigned num;
bool active;
unsigned readCount;
unsigned writeCount;
bool HasValue();
UINT64 GetValue();
bool CheckRead(UINT32 cAddr, unsigned cDataSize, UINT64 data);
bool CheckWrite(UINT32 cAddr, unsigned cDataSize, UINT64 data);
bool CheckInput(CIO *cIO, UINT64 data);
bool CheckOutput(CIO *cIO, UINT64 data);
virtual bool CheckBreak(bool isRead, UINT64 data) = 0;
virtual void Reset();
virtual bool GetInfo(char *str);
};
/*
* Simple watch that will always halt execution when read from and/or written to.
*/
class CSimpleWatch : public CWatch
{
public:
CSimpleWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite);
CSimpleWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput);
bool CheckBreak(bool isRead, UINT64 data);
};
/*
* Count watch that will halt execution after it has been read from and/or written to a certain number of times.
*/
class CCountWatch : public CWatch
{
private:
unsigned m_count;
unsigned m_counter;
public:
CCountWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned count);
CCountWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput, unsigned count);
bool CheckBreak(bool isRead, UINT64 data);
void Reset();
bool GetInfo(char *str);
};
/*
* Match watch that will halt execution after a particular data sequence has been read from and/or written to it.
*/
class CMatchWatch : public CWatch
{
private:
unsigned m_dataLen;
UINT64 *m_dataArray;
unsigned m_counter;
public:
CMatchWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, std::vector<UINT64> &dataSeq);
CMatchWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput, std::vector<UINT64> &dataSeq);
~CMatchWatch();
bool CheckBreak(bool isRead, UINT64 data);
void Reset();
bool GetInfo(char *str);
};
/*
* Capture watch that will halt not execution but will keep track of all data read from and/or written to it.
*/
class CCaptureWatch : public CWatch
{
private:
unsigned m_maxLen;
unsigned m_len;
unsigned m_index;
UINT64 m_dataVals[255];
bool m_dataRW[255];
public:
CCaptureWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned wMaxLen);
CCaptureWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput, unsigned wMaxLen);
bool CheckBreak(bool isRead, UINT64 data);
void Reset();
bool GetInfo(char *str);
};
/*
* Print watch that will halt not execution but will output all data read from and/or written to it.
*/
class CPrintWatch : public CWatch
{
public:
CPrintWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite);
CPrintWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput);
bool CheckBreak(bool isRead, UINT64 data);
};
}
#endif // INCLUDED_WATCH_H
#endif // SUPERMODEL_DEBUGGER
+48
View File
@@ -0,0 +1,48 @@
#ifndef INCLUDED_GAME_H
#define INCLUDED_GAME_H
#include <string>
#include <memory>
struct Game
{
std::string name;
std::string parent;
std::string title;
std::string version;
std::string manufacturer;
unsigned year = 0;
std::string stepping;
std::string mpeg_board;
uint32_t encryption_key = 0;
enum Inputs
{
INPUT_UI = 0, // special code reserved for Supermodel UI inputs
INPUT_COMMON = 0x00000001, // common controls (coins, service, test)
INPUT_VEHICLE = 0x00000002, // vehicle controls
INPUT_JOYSTICK1 = 0x00000004, // joystick 1
INPUT_JOYSTICK2 = 0x00000008, // joystick 2
INPUT_FIGHTING = 0x00000010, // fighting game controls
INPUT_VR4 = 0x00000020, // four VR view buttons
INPUT_VIEWCHANGE = 0x00000040, // single view change button
INPUT_SHIFT4 = 0x00000080, // 4-speed shifter
INPUT_SHIFTUPDOWN = 0x00000100, // up/down shifter
INPUT_HANDBRAKE = 0x00000200, // handbrake
INPUT_HARLEY = 0x00000400, // Harley Davidson controls
INPUT_GUN1 = 0x00000800, // light gun 1
INPUT_GUN2 = 0x00001000, // light gun 2
INPUT_ANALOG_JOYSTICK = 0x00002000, // analog joystick
INPUT_TWIN_JOYSTICKS = 0x00004000, // twin joysticks
INPUT_SOCCER = 0x00008000, // soccer controls
INPUT_SPIKEOUT = 0x00010000, // Spikeout buttons
INPUT_ANALOG_GUN1 = 0x00020000, // analog gun 1
INPUT_ANALOG_GUN2 = 0x00040000, // analog gun 2
INPUT_SKI = 0x00080000, // ski controls
INPUT_MAGTRUCK = 0x00100000, // magical truck controls
INPUT_FISHING = 0x00200000, // fishing controls
INPUT_ALL = 0x003FFFFF
};
uint32_t inputs = 0;
};
#endif // INCLUDED_GAME_H
+849
View File
@@ -0,0 +1,849 @@
#include "GameLoader.h"
#include "OSD/Logger.h"
#include "Util/NewConfig.h"
#include "Util/ConfigBuilders.h"
#include "Util/ByteSwap.h"
#include "Util/Format.h"
#include <algorithm>
#include <cstring>
#include <iostream>
bool GameLoader::LoadZipArchive(ZipArchive *zip, const std::string &zipfilename) const
{
unzFile zf = unzOpen(zipfilename.c_str());
if (NULL == zf)
{
ErrorLog("Could not open '%s'.", zipfilename.c_str());
return true;
}
zip->zipfilenames.push_back(zipfilename);
zip->zfs.push_back(zf);
// Identify all files in zip archive
int err = UNZ_OK;
for (err = unzGoToFirstFile(zf); err == UNZ_OK; err = unzGoToNextFile(zf))
{
unz_file_info file_info;
char filename_buffer[256];
if (UNZ_OK != unzGetCurrentFileInfo(zf, &file_info, filename_buffer, sizeof(filename_buffer), NULL, 0, NULL, 0))
continue;
zip->files_by_crc[file_info.crc].zf = zf;
zip->files_by_crc[file_info.crc].zipfilename = filename_buffer;
zip->files_by_crc[file_info.crc].filename = filename_buffer;
zip->files_by_crc[file_info.crc].uncompressed_size = file_info.uncompressed_size;
zip->files_by_crc[file_info.crc].crc32 = file_info.crc;
}
if (err != UNZ_END_OF_LIST_OF_FILE)
{
ErrorLog("Unable to read the contents of '%s' (code 0x%x).", zipfilename.c_str(), err);
return true;
}
InfoLog("Opened %s.", zipfilename.c_str());
return false;
}
bool GameLoader::FileExistsInZipArchive(const File::ptr_t &file, const ZipArchive &zip) const
{
if (file->has_crc32)
{
auto it = zip.files_by_crc.find(file->crc32);
return it != zip.files_by_crc.end();
}
// Try to lookup by name
for (auto &v: zip.files_by_crc)
{
if (Util::ToLower(v.second.filename) == file->filename)
return true;
}
return false;
}
const GameLoader::ZippedFile *GameLoader::LookupFile(const File::ptr_t &file, const ZipArchive &zip) const
{
if (file->has_crc32)
{
auto it = zip.files_by_crc.find(file->crc32);
if (it == zip.files_by_crc.end())
{
if (zip.zfs.size() == 1)
ErrorLog("'%s' with CRC32 0x%08x not found in '%s'.", file->filename.c_str(), file->crc32, zip.zipfilenames[0].c_str());
else
ErrorLog("'%s' with CRC32 0x%08x not found in '%s'.", file->filename.c_str(), file->crc32, Util::Format("', '").Join(zip.zipfilenames).str().c_str());
return nullptr;
}
return &it->second;
}
// Try to lookup by name
for (auto &v: zip.files_by_crc)
{
if (Util::ToLower(v.second.filename) == file->filename)
return &v.second;
}
if (zip.zfs.size() == 1)
ErrorLog("'%s' not found in '%s'.", file->filename.c_str(), zip.zipfilenames[0].c_str());
else
ErrorLog("'%s' not found in '%s'.", file->filename.c_str(), Util::Format("', '").Join(zip.zipfilenames).str().c_str());
return nullptr;
}
bool GameLoader::LoadZippedFile(std::shared_ptr<uint8_t> *buffer, size_t *file_size, const GameLoader::File::ptr_t &file, const ZipArchive &zip) const
{
// Locate file
const ZippedFile *zipped_file = LookupFile(file, zip);
if (!zipped_file)
return true;
if (UNZ_OK != unzLocateFile(zipped_file->zf, zipped_file->filename.c_str(), 2))
{
ErrorLog("Unable to locate '%s' in '%s'. Is zip file corrupt?", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
return true;
}
// Read it in
if (UNZ_OK != unzOpenCurrentFile(zipped_file->zf))
{
ErrorLog("Unable to read '%s' from '%s'. Is zip file corrupt?", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
return true;
}
*file_size = zipped_file->uncompressed_size;
buffer->reset(new uint8_t[*file_size], std::default_delete<uint8_t[]>());
ZPOS64_T bytes_read = unzReadCurrentFile(zipped_file->zf, buffer->get(), *file_size);
if (bytes_read != *file_size)
{
ErrorLog("Unable to read '%s' from '%s'. Is zip file corrupt?", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
unzCloseCurrentFile(zipped_file->zf);
return true;
}
// And close it
if (UNZ_CRCERROR == unzCloseCurrentFile(zipped_file->zf))
ErrorLog("CRC error reading '%s' from '%s'. File may be corrupt.", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
return false;
}
bool GameLoader::MissingAttrib(const GameLoader &loader, const Util::Config::Node &node, const std::string &attribute)
{
if (node[attribute].Empty())
{
ErrorLog("%s: <%s> tag is missing required attribute '%s'.", loader.m_xml_filename.c_str(), node.Key().c_str(), attribute.c_str());
return true;
}
return false;
}
GameLoader::File::ptr_t GameLoader::File::Create(const GameLoader &loader, const Util::Config::Node &file_node)
{
if (GameLoader::MissingAttrib(loader, file_node, "name") | GameLoader::MissingAttrib(loader, file_node, "offset"))
return ptr_t();
ptr_t file = std::make_shared<File>();
file->offset = file_node["offset"].ValueAs<uint32_t>();
file->filename = Util::ToLower(file_node["name"].ValueAs<std::string>());
file->has_crc32 = file_node["crc32"].Exists();
file->crc32 = file->has_crc32 ? file_node["crc32"].ValueAs<uint32_t>() : 0;
return file;
}
bool GameLoader::File::Matches(const std::string &filename_to_match, uint32_t crc32_to_match) const
{
if (has_crc32)
return crc32_to_match == crc32;
return Util::ToLower(filename_to_match) == filename;
}
bool GameLoader::File::operator==(const File &rhs) const
{
return Matches(rhs.filename, rhs.crc32);
}
GameLoader::Region::ptr_t GameLoader::Region::Create(const GameLoader &loader, const Util::Config::Node &region_node)
{
if (GameLoader::MissingAttrib(loader, region_node, "name") | MissingAttrib(loader, region_node, "stride") | GameLoader::MissingAttrib(loader, region_node, "chunk_size"))
return ptr_t();
ptr_t region = std::make_shared<Region>();
region->region_name = region_node["name"].Value<std::string>();
region->stride = region_node["stride"].ValueAs<size_t>();
region->chunk_size = region_node["chunk_size"].ValueAs<size_t>();
region->byte_swap = region_node["byte_swap"].ValueAsDefault<bool>(false);
return region;
}
bool GameLoader::Region::AttribsMatch(const ptr_t &other) const
{
return stride == other->stride && chunk_size == other->chunk_size && byte_swap == other->byte_swap;
}
bool GameLoader::Region::FindFileIndexByOffset(size_t *idx, uint32_t offset) const
{
for (size_t i = 0; i < files.size(); i++)
{
if (files[i]->offset == offset)
{
*idx = i;
return true;
}
}
return false;
}
static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
{
game->name = game_node["name"].ValueAs<std::string>();
game->parent = game_node["parent"].ValueAsDefault<std::string>(std::string());
game->title = game_node["identity/title"].ValueAsDefault<std::string>("Unknown");
game->version = game_node["identity/version"].ValueAsDefault<std::string>("");
game->manufacturer = game_node["identity/manufacturer"].ValueAsDefault<std::string>("Unknown");
game->year = game_node["identity/year"].ValueAsDefault<unsigned>(0);
game->stepping = game_node["hardware/stepping"].ValueAsDefault<std::string>("");
game->mpeg_board = game_node["hardware/mpeg_board"].ValueAsDefault<std::string>("");
game->encryption_key = game_node["hardware/encryption_key"].ValueAsDefault<uint32_t>(0);
std::map<std::string, uint32_t> input_flags
{
{ "common", Game::INPUT_COMMON },
{ "vehicle", Game::INPUT_VEHICLE },
{ "joystick1", Game::INPUT_JOYSTICK1 },
{ "joystick2", Game::INPUT_JOYSTICK2 },
{ "fighting", Game::INPUT_FIGHTING },
{ "vr4", Game::INPUT_VR4 },
{ "viewchange", Game::INPUT_VIEWCHANGE },
{ "shift4", Game::INPUT_SHIFT4 },
{ "shiftupdown", Game::INPUT_SHIFTUPDOWN },
{ "handbrake", Game::INPUT_HANDBRAKE },
{ "harley", Game::INPUT_HARLEY },
{ "gun1", Game::INPUT_GUN1 },
{ "gun2", Game::INPUT_GUN2 },
{ "analog_joystick", Game::INPUT_ANALOG_JOYSTICK },
{ "twin_joysticks", Game::INPUT_TWIN_JOYSTICKS },
{ "soccer", Game::INPUT_SOCCER },
{ "spikeout", Game::INPUT_SPIKEOUT },
{ "analog_gun1", Game::INPUT_ANALOG_GUN1 },
{ "analog_gun2", Game::INPUT_ANALOG_GUN2 },
{ "ski", Game::INPUT_SKI },
{ "magtruck", Game::INPUT_MAGTRUCK },
{ "fishing", Game::INPUT_FISHING }
};
for (auto &node: game_node["hardware/inputs"])
{
if (node.Key() == "input" && node["type"].Exists())
{
const std::string input_type = node["type"].ValueAs<std::string>();
game->inputs |= input_flags[input_type];
}
}
}
bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
{
for (auto it = xml.begin(); it != xml.end(); ++it)
{
// Root games node
auto &root_node = *it;
if (root_node.Key() != "games")
continue;
for (auto &game_node: root_node)
{
// Game node
if (game_node.Key() != "game")
continue;
if (game_node["name"].Empty())
{
//TODO: associate line numbers in config
//ErrorLog("%s: Ignoring <game> tag with missing 'name' attribute.", m_xml_filename.c_str());
continue;
}
std::string game_name = game_node["name"].ValueAs<std::string>();
if (m_regions_by_game.find(game_name) != m_regions_by_game.end())
{
ErrorLog("%s: Ignoring redefinition of game '%s'.", m_xml_filename.c_str(), game_name.c_str());
continue;
}
RegionsByName_t &regions_by_name = m_regions_by_game[game_name];
PatchesByRegion_t &patches_by_region = m_patches_by_game[game_name];
PopulateGameInfo(&m_game_info_by_game[game_name], game_node);
for (auto &roms_node: game_node)
{
if (roms_node.Key() != "roms")
continue;
/*
* Regions define contiguous memory areas that individual ROM files are
* loaded into. It is possible to have multiple region tags identifying
* the same region. They will be aggregated. This is useful for parent
* and child ROM sets, which each may need to define the same region,
* with the child set loading in different files to overwrite the parent
* set.
*/
for (auto &region_node: roms_node)
{
if (region_node.Key() != "region")
continue;
// Look up region structure or create new one if needed
std::string region_name = region_node["name"].Value<std::string>();
auto it = regions_by_name.find(region_name);
Region::ptr_t region = (it != regions_by_name.end()) ? it->second : Region::Create(*this, region_node);
if (!region)
continue;
/*
* Files are defined by the offset they are loaded at. Normally, there
* should be one file per offset but parent/child ROM sets will violate
* this, and so it is allowed.
*/
std::vector<File::ptr_t> &files = region->files;
for (auto &file_node: region_node)
{
if (file_node.Key() != "file")
continue;
File::ptr_t file = File::Create(*this, file_node);
if (!file)
continue;
files.push_back(file);
}
// Check to ensure that some files were defined in the region
if (files.empty())
ErrorLog("%s: No files defined in region '%s' of '%s'.", m_xml_filename.c_str(), region->region_name.c_str(), game_name.c_str());
else
regions_by_name[region->region_name] = region;
}
// ROM patches, if any
for (auto &patches_node: roms_node)
{
if (patches_node.Key() != "patches")
continue;
for (auto &patch_node: patches_node)
{
if (MissingAttrib(*this, patch_node, "region") ||
MissingAttrib(*this, patch_node, "bits") ||
MissingAttrib(*this, patch_node, "offset") ||
MissingAttrib(*this, patch_node, "value"))
continue;
std::string region = patch_node["region"].ValueAs<std::string>();
unsigned bits = patch_node["bits"].ValueAs<unsigned>();
uint32_t offset = patch_node["offset"].ValueAs<uint32_t>();
uint64_t value = patch_node["value"].ValueAs<uint64_t>();
if (bits != 8 && bits != 16 && bits != 32 && bits != 64)
ErrorLog("%s: Ignoring ROM patch in '%s' with invalid bit length. Must be 8, 16, 32, or 64!", m_xml_filename.c_str(), game_name.c_str());
else
patches_by_region[region].push_back(ROM::BigEndianPatch(offset, value, bits));
}
}
}
// Check to ensure that some ROM regions were defined for the game
if (regions_by_name.empty())
ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str());
}
}
// Check to ensure some games were defined
if (m_regions_by_game.empty())
{
ErrorLog("%s: No games defined.", m_xml_filename.c_str());
return true;
}
return false;
}
static bool IsChildSet(const Game &game)
{
return game.parent.length() > 0;
}
bool GameLoader::MergeChildrenWithParents()
{
bool error = false;
for (auto &v1: m_regions_by_game)
{
auto &game = m_game_info_by_game[v1.first];
if (!IsChildSet(game)) // we want child sets
continue;
auto &child_regions = v1.second;
auto &parent_regions = m_regions_by_game[game.parent];
// Rebuild child regions by copying over all parent regions first, then
// merge in files from equivalent child regions
RegionsByName_t new_regions;
for (auto &v2: parent_regions)
{
// Copy over parent region (shallow copy is sufficient, vector of files
// will be copied over correctly)
auto &region_name = v2.first;
new_regions[region_name] = std::make_shared<Region>(*v2.second);
auto &new_region = new_regions[region_name];
// Replace equivalent files from child in parent region, appending any
// new ones
if (child_regions.find(region_name) != child_regions.end())
{
auto &child_region = child_regions[region_name];
if (!new_region->AttribsMatch(child_region))
{
ErrorLog("%s: Attributes of region '%s' in parent '%s' and child '%s' do not match.", m_xml_filename.c_str(), region_name.c_str(), game.parent.c_str(), game.name.c_str());
error = true;
}
for (size_t i = 0; i < child_region->files.size(); i++)
{
size_t idx;
if (new_region->FindFileIndexByOffset(&idx, child_region->files[i]->offset))
new_region->files[idx] = child_region->files[i];
else
new_region->files.push_back(child_region->files[i]);
}
}
}
// Simply append any region in child that does *not* exist in parent
for (auto &v2: child_regions)
{
if (new_regions.find(v2.first) == new_regions.end())
{
// Since these are pointers anyway, just insert directly
new_regions[v2.first] = v2.second;
}
}
// Save the final result
m_regions_by_merged_game[v1.first] = new_regions;
}
return error;
}
void GameLoader::LogROMDefinition(const std::string &game_name, const RegionsByName_t &regions_by_name) const
{
InfoLog("%s:", game_name.c_str());
for (auto &v2: regions_by_name)
{
InfoLog(" %s: stride=%zu, chunk size=%zu, byte swap=%d", v2.first.c_str(), v2.second->stride, v2.second->chunk_size, v2.second->byte_swap ? 1 : 0);
for (auto &file: v2.second->files)
{
InfoLog(" %s, crc32=0x%08x, offset=0x%08x", file->filename.c_str(), file->crc32, file->offset);
}
}
}
bool GameLoader::ParseXML(const Util::Config::Node &xml)
{
if (LoadGamesFromXML(xml))
return true;
// More than one level of parents not allowed
bool error = false;
std::set<std::string> parents_with_parents;
for (auto &v: m_game_info_by_game)
{
if (IsChildSet(v.second))
{
if (IsChildSet(m_game_info_by_game[v.second.parent]))
{
parents_with_parents.insert(v.second.parent);
error = true;
}
}
}
for (auto &game_name: parents_with_parents)
{
ErrorLog("%s: Parent ROM set '%s' also has parent defined, which is not allowed.", m_xml_filename.c_str(), game_name.c_str());
}
if (MergeChildrenWithParents())
return true;
return error;
}
bool GameLoader::LoadDefinitionXML(const std::string &filename)
{
m_xml_filename = filename;
Util::Config::Node xml("xml");
if (Util::Config::FromXMLFile(&xml, filename))
{
ErrorLog("Game and ROM set definitions could not be loaded! ROMs will not be detected.");
return true;
}
return ParseXML(xml);
}
void GameLoader::FindEquivalentFiles(std::set<File::ptr_t> *equivalent_files, const std::set<File::ptr_t> &a, const std::set<File::ptr_t> &b)
{
// Copy files that are equivalent between a and b from a (doesn't matter
// which we actually use) to output
for (auto &file1: a)
{
for (auto &file2: b)
{
if (*file1 == *file2)
equivalent_files->insert(file1);
}
}
}
void GameLoader::IdentifyGamesInZipArchive(
std::set<std::string> *complete_games,
std::map<std::string, std::set<File::ptr_t>> *files_missing_by_game,
const ZipArchive &zip,
const std::map<std::string, RegionsByName_t> &regions_by_game) const
{
std::map<std::string, std::set<File::ptr_t>> files_required_by_game;
std::map<std::string, std::set<File::ptr_t>> files_found_by_game;
// Determine which files each game requires and which files are present in
// the zip archive
for (auto &v1: regions_by_game)
{
const std::string &game_name = v1.first;
auto &regions_by_name = v1.second;
for (auto &v2: regions_by_name)
{
Region::ptr_t region = v2.second;
for (auto file: region->files)
{
// Add each file to the set of required files per game
files_required_by_game[game_name].insert(file);
// Check file in ROM definition against all files in zip
if (FileExistsInZipArchive(file, zip))
files_found_by_game[game_name].insert(file);
}
}
}
/*
* Corner case: some child ROM sets legitimately share files, which can fool
* us into thinking two games are partially present. Need to remove the one
* that is not really there by detecting case when only overlapping files
* exist (the ROM set with more present files is the intended one).
*/
std::vector<std::string> to_remove;
for (auto &v1: files_found_by_game)
{
auto &game1_name = v1.first;
auto &game1_files = v1.second;
for (auto &v2: files_found_by_game)
{
auto &game2_name = v2.first;
auto &game2_files = v2.second;
if (game1_name == game2_name)
continue;
std::set<File::ptr_t> equivalent_files;
FindEquivalentFiles(&equivalent_files, game1_files, game2_files);
/*
* If the these two games have a different number of files in the zip
* archive, but one consists only of the overlapping files, we can safely
* conclude that these files represent only the game with the larger
* number of files present. Otherwise, if only the overlapping files are
* present for both, we have a genuine ambiguity and hence do nothing.
*/
if (game1_files.size() != game2_files.size() && equivalent_files.size() == game2_files.size())
to_remove.push_back(game2_name);
}
}
for (auto &game_name: to_remove)
{
files_found_by_game.erase(game_name);
}
// Find the missing files for each game we found in the zip archive, then use
// this to determine whether the complete game exists
auto compare = [](const File::ptr_t &a, const File::ptr_t &b) { return a->filename < b->filename; };
for (auto &v: files_found_by_game)
{
auto &files_found = v.second;
auto &files_required = files_required_by_game[v.first];
auto &files_missing = (*files_missing_by_game)[v.first];
// Need to sort by filename for set_difference to work
std::vector<File::ptr_t> files_found_v(files_found.begin(), files_found.end());
std::vector<File::ptr_t> files_required_v(files_required.begin(), files_required.end());
std::sort(files_found_v.begin(), files_found_v.end(), compare);
std::sort(files_required_v.begin(), files_required_v.end(), compare);
// Use set difference to find missing files
std::set_difference(
files_required_v.begin(), files_required_v.end(),
files_found_v.begin(), files_found_v.end(),
std::inserter(files_missing, files_missing.end()),
compare);
// Is the whole game present?
if (files_found == files_required)
complete_games->insert(v.first);
// Clean up: if no files missing, don't want empty entry in map
if (files_missing.empty())
files_missing_by_game->erase(v.first);
}
}
void GameLoader::ChooseGameInZipArchive(std::string *chosen_game, bool *missing_parent_roms, const ZipArchive &zip, const std::string &zipfilename) const
{
chosen_game->clear();
*missing_parent_roms = false;
// Find complete unmerged games (those that do not need to be merged with a
// parent). This will pick up child-only ROMs, too, which we prune out later.
std::set<std::string> complete_games;
std::map<std::string, std::set<File::ptr_t>> files_missing_by_game;
IdentifyGamesInZipArchive(&complete_games, &files_missing_by_game, zip, m_regions_by_game);
// Find complete, merged games
std::set<std::string> complete_merged_games;
std::map<std::string, std::set<File::ptr_t>> files_missing_by_merged_game;
IdentifyGamesInZipArchive(&complete_merged_games, &files_missing_by_merged_game, zip, m_regions_by_merged_game);
/*
* Find incomplete child games by sorting child games out from the unmerged
* games results and pruning out complete merged games. Don't care about
* missing files because they are not neccessarily an error for these games.
* If one ends up being chosen, we would try to load from a second, parent
* ROM set.
*/
std::set<std::string> incomplete_child_games;
for (auto &v: m_game_info_by_game)
{
auto &game_name = v.first;
if (IsChildSet(v.second))
{
if (complete_games.count(game_name) || files_missing_by_game.find(game_name) != files_missing_by_game.end())
{
incomplete_child_games.insert(game_name);
complete_games.erase(game_name);
files_missing_by_game.erase(game_name);
}
}
}
for (auto &game_name: complete_merged_games)
{
incomplete_child_games.erase(game_name);
}
// Complete merged games take highest precedence
for (auto &game_name: complete_merged_games)
{
const std::string &parent = m_game_info_by_game.find(game_name)->second.parent;
// Complete merged game will be used, so ignore the parent entirely
complete_games.erase(parent);
// Complete merged sets will often have some parent ROMs missing (those
// replaced by the child games). This is not an error, so remove parents of
// complete merged games from missing file list.
files_missing_by_game.erase(parent);
}
// Any remaining incomplete games from the unmerged set are legitimate errors
for (auto &v: files_missing_by_game)
{
for (auto &file: v.second)
{
ErrorLog("'%s' (CRC32 0x%08x) not found in '%s' for game '%s'.", file->filename.c_str(), file->crc32, zipfilename.c_str(), v.first.c_str());
}
ErrorLog("Ignoring game '%s' in '%s' because it is missing files.", v.first.c_str(), zipfilename.c_str());
}
// Choose game: complete merged game > incomplete child game > complete
// unmerged game
if (!complete_merged_games.empty())
chosen_game->assign(*complete_merged_games.begin());
else if (!incomplete_child_games.empty())
{
// TODO: could use scoring to pick game with most files?
chosen_game->assign(*incomplete_child_games.begin());
*missing_parent_roms = true; // try to find missing files in parent ROM zip file
}
else if (!complete_games.empty())
chosen_game->assign(*complete_games.begin());
else
{
ErrorLog("No complete Model 3 games found in '%s'.", zipfilename.c_str());
return;
}
// Print out which game we chose from valid candidates in the zip file
std::set<std::string> candidates(complete_games);
candidates.insert(complete_merged_games.begin(), complete_merged_games.end());
candidates.insert(incomplete_child_games.begin(), incomplete_child_games.end());
if (candidates.size() > 1)
ErrorLog("Multiple games found in '%s' (%s). Loading '%s'.", zipfilename.c_str(), Util::Format(", ").Join(candidates).str().c_str(), chosen_game->c_str());
}
bool GameLoader::ComputeRegionSize(uint32_t *region_size, const GameLoader::Region::ptr_t &region, const ZipArchive &zip) const
{
// Files in region need not be loaded contiguously. To find region size,
// use maximum end_addr = offset + stride * (num_chunks - 1) + chunk_size.
std::vector<uint32_t> end_addr;
bool error = false;
for (auto file: region->files)
{
const ZippedFile *zipped_file = LookupFile(file, zip);
if (zipped_file)
{
if (zipped_file->uncompressed_size % region->chunk_size != 0)
{
ErrorLog("File '%s' in '%s' is not sized in %d-byte chunks.", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str(), region->chunk_size);
error = true;
}
uint32_t num_chunks = (uint32_t)(zipped_file->uncompressed_size / region->chunk_size);
end_addr.push_back(file->offset + region->stride * (num_chunks - 1) + region->chunk_size);
}
else
error = true;
}
if (!error)
*region_size = *std::max_element(end_addr.begin(), end_addr.end());
return error;
}
// We need to preserve the absolute offsets in order for byte swapping to work
// properly when chunk size is 1
static inline void CopyBytes(uint8_t *dest_base, uint32_t dest_offset, const uint8_t *src_base, uint32_t src_offset, uint32_t size, uint32_t byte_swap)
{
for (uint32_t i = 0; i < size; i++)
{
dest_base[(dest_offset + i) ^ byte_swap] = src_base[src_offset + i];
}
}
bool GameLoader::LoadRegion(ROM *rom, const GameLoader::Region::ptr_t &region, const ZipArchive &zip) const
{
bool error = false;
for (auto &file: region->files)
{
std::shared_ptr<uint8_t> tmp;
size_t file_size;
error |= LoadZippedFile(&tmp, &file_size, file, zip);
if (!error)
{
uint8_t *dest = rom->data.get();
uint8_t *src = tmp.get();
if (region->chunk_size == region->stride)
{
memcpy(dest + file->offset, src, file_size);
if (region->byte_swap)
Util::FlipEndian16(dest + file->offset, file_size);
}
else
{
uint32_t num_chunks = (uint32_t)file_size / region->chunk_size;
uint32_t dest_offset = file->offset;
uint32_t src_offset = 0;
uint32_t chunk_size = (uint32_t)region->chunk_size; // cache these as pointer dereferencing cripples performance in a tight loop
uint32_t stride = (uint32_t)region->stride;
uint32_t byte_swap = region->byte_swap;
for (uint32_t i = 0; i < num_chunks; i++)
{
CopyBytes(dest, dest_offset, src, src_offset, chunk_size, byte_swap);
dest_offset += stride;
src_offset += chunk_size;
}
}
}
}
return error;
}
bool GameLoader::LoadROMs(ROMSet *rom_set, const std::string &game_name, const ZipArchive &zip) const
{
auto it = m_game_info_by_game.find(game_name);
if (it == m_game_info_by_game.end())
{
ErrorLog("Cannot load unknown game '%s'. Is it defined in '%s'?", game_name.c_str(), m_xml_filename.c_str());
return true;
}
// Load up the ROMs
auto &regions_by_name = IsChildSet(it->second) ? m_regions_by_merged_game.find(game_name)->second : m_regions_by_game.find(game_name)->second;
LogROMDefinition(game_name, regions_by_name);
bool error = false;
for (auto &v: regions_by_name)
{
auto &region = v.second;
uint32_t region_size = 0;
if (ComputeRegionSize(&region_size, region, zip))
error |= true;
else
{
// Load up the ROM region
auto &rom = rom_set->rom_by_region[region->region_name];
rom.data.reset(new uint8_t[region_size], std::default_delete<uint8_t[]>());
rom.size = region_size;
error |= LoadRegion(&rom, region, zip);
}
}
// Attach the patches and do some more error checking here
auto &patches_by_region = m_patches_by_game.find(game_name)->second;
for (auto &v: patches_by_region)
{
auto &region_name = v.first;
auto &patches = v.second;
if (regions_by_name.find(region_name) == regions_by_name.end())
ErrorLog("%s: Ignoring ROM patch for undefined region '%s' in '%s'.", m_xml_filename.c_str(), region_name.c_str(), game_name.c_str());
else if (rom_set->rom_by_region.find(region_name) != rom_set->rom_by_region.end())
rom_set->rom_by_region[region_name].patches = patches;
}
return error;
}
std::string StripFilename(const std::string &filepath)
{
// Search for last '/' or '\', if any
size_t last_slash = std::string::npos;
for (size_t i = filepath.length() - 1; i < filepath.length(); i--)
{
if (filepath[i] == '/' || filepath[i] =='\\')
{
last_slash = i;
break;
}
}
// If none found, there is directory component here
if (last_slash == std::string::npos)
return "";
// Otherwise, strip everything after the slash
return std::string(filepath, 0, last_slash + 1);
}
bool GameLoader::Load(Game *game, ROMSet *rom_set, const std::string &zipfilename) const
{
*game = Game();
// Read the zip contents
ZipArchive zip;
if (LoadZipArchive(&zip, zipfilename))
return true;
// Pick the game to load (there could be multiple ROM sets in a zip file)
std::string chosen_game;
bool missing_parent_roms = false;
ChooseGameInZipArchive(&chosen_game, &missing_parent_roms, zip, zipfilename);
if (chosen_game.empty())
return true;
// Return game information to caller
*game = m_game_info_by_game.find(chosen_game)->second;
// Bring in additional parent ROM set if needed
if (missing_parent_roms)
{
std::string parent_zipfilename = StripFilename(zipfilename) + game->parent + ".zip";
if (LoadZipArchive(&zip, parent_zipfilename))
{
ErrorLog("Expected to find parent ROM set of '%s' at '%s'.", game->name.c_str(), parent_zipfilename.c_str());
return true;
}
}
// Load
bool error = LoadROMs(rom_set, game->name, zip);
if (error)
*game = Game();
return error;
}
GameLoader::GameLoader(const std::string &xml_file)
{
LoadDefinitionXML(xml_file);
}
+113
View File
@@ -0,0 +1,113 @@
#ifndef INCLUDED_GAMELOADER_H
#define INCLUDED_GAMELOADER_H
#include "Util/NewConfig.h"
#include "Pkgs/unzip.h"
#include "Game.h"
#include "ROMSet.h"
#include <map>
#include <set>
class GameLoader
{
private:
// Describes a file node in the game XML
struct File
{
typedef std::shared_ptr<File> ptr_t;
uint32_t offset;
std::string filename;
uint32_t crc32;
bool has_crc32;
static ptr_t Create(const GameLoader &loader, const Util::Config::Node &file_node);
bool Matches(const std::string &filename, uint32_t crc32) const;
bool operator==(const File &rhs) const;
};
// Describes a region node in the game XML
struct Region
{
typedef std::shared_ptr<Region> ptr_t;
std::string region_name;
size_t stride;
size_t chunk_size;
bool byte_swap;
std::vector<File::ptr_t> files;
static ptr_t Create(const GameLoader &loader, const Util::Config::Node &region_node);
bool AttribsMatch(const ptr_t &other) const;
bool FindFileIndexByOffset(size_t *idx, uint32_t offset) const;
};
// Game information from XML
std::map<std::string, Game> m_game_info_by_game;
// ROM patches by game and by region. Cannot place into Region because child
// sets do not inherit parent patches, which may complicate merging.
typedef std::map<std::string, std::vector<ROM::BigEndianPatch>> PatchesByRegion_t;
std::map<std::string, PatchesByRegion_t> m_patches_by_game;
// Parsed XML
typedef std::map<std::string, Region::ptr_t> RegionsByName_t;
std::map<std::string, RegionsByName_t> m_regions_by_game; // all games as defined in XML
std::map<std::string, RegionsByName_t> m_regions_by_merged_game; // only child sets merged w/ parents
std::string m_xml_filename;
// Single compressed file inside of a zip archive
struct ZippedFile
{
unzFile zf = nullptr;
std::string zipfilename; // zip archive
std::string filename; // file inside the zip archive
size_t uncompressed_size = 0;
uint32_t crc32 = 0;
};
// Multiple zip archives
struct ZipArchive
{
std::vector<std::string> zipfilenames;
std::vector<unzFile> zfs;
std::map<uint32_t, ZippedFile> files_by_crc;
~ZipArchive()
{
for (auto &zf: zfs)
{
unzClose(zf);
}
}
};
bool LoadZipArchive(ZipArchive *zip, const std::string &zipfilename) const;
const ZippedFile *LookupFile(const File::ptr_t &file, const ZipArchive &zip) const;
bool FileExistsInZipArchive(const File::ptr_t &file, const ZipArchive &zip) const;
bool LoadZippedFile(std::shared_ptr<uint8_t> *buffer, size_t *file_size, const GameLoader::File::ptr_t &file, const ZipArchive &zip) const;
static bool MissingAttrib(const GameLoader &loader, const Util::Config::Node &node, const std::string &attribute);
bool LoadGamesFromXML(const Util::Config::Node &xml);
bool MergeChildrenWithParents();
void LogROMDefinition(const std::string &game_name, const RegionsByName_t &regions_by_name) const;
bool ParseXML(const Util::Config::Node &xml);
bool LoadDefinitionXML(const std::string &filename);
static void FindEquivalentFiles(std::set<File::ptr_t> *equivalent_files, const std::set<File::ptr_t> &a, const std::set<File::ptr_t> &b);
void IdentifyGamesInZipArchive(
std::set<std::string> *complete_games,
std::map<std::string, std::set<File::ptr_t>> *files_missing_by_game,
const ZipArchive &zip,
const std::map<std::string, RegionsByName_t> &regions_by_game) const;
bool ComputeRegionSize(uint32_t *region_size, const Region::ptr_t &region, const ZipArchive &zip) const;
void ChooseGameInZipArchive(std::string *chosen_game, bool *missing_parent_roms, const ZipArchive &zip, const std::string &zipfilename) const;
bool LoadRegion(ROM *buffer, const GameLoader::Region::ptr_t &region, const ZipArchive &zip) const;
bool LoadROMs(ROMSet *rom_set, const std::string &game_name, const ZipArchive &zip) const;
std::string ChooseGame(const std::set<std::string> &games_found, const std::string &zipfilename) const;
static bool CompareFilesByName(const File::ptr_t &a,const File::ptr_t &b);
public:
GameLoader(const std::string &xml_file);
bool Load(Game *game, ROMSet *rom_set, const std::string &zipfilename) const;
const std::map<std::string, Game> &GetGames() const
{
return m_game_info_by_game;
}
};
#endif // INCLUDED_GAMELOADER_H
BIN
View File
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
#ifndef INCLUDED_IRENDER3D_H
#define INCLUDED_IRENDER3D_H
#include <cstdint>
/*
* IRender3D:
*
* Interface (abstract base class) for Real3D rendering engine.
*/
class IRender3D
{
public:
virtual void RenderFrame(void) = 0;
virtual void BeginFrame(void) = 0;
virtual void EndFrame(void) = 0;
virtual void UploadTextures(unsigned level, unsigned x, unsigned y, unsigned width, unsigned height) = 0;
virtual void AttachMemory(const uint32_t *cullingRAMLoPtr, const uint32_t *cullingRAMHiPtr, const uint32_t *polyRAMPtr, const uint32_t *vromPtr, const uint16_t *textureRAMPtr) = 0;
virtual void SetStepping(int stepping) = 0;
virtual bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes) = 0;
virtual void SetSunClamp(bool enable) = 0;
virtual void SetSignedShade(bool enable) = 0;
virtual ~IRender3D()
{
}
};
#endif // INCLUDED_IRENDER3D_H
+67
View File
@@ -0,0 +1,67 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Error.cpp
*
* Error reporting for 3D renderer. There are some situations in which it is
* appropriate to inform the user of an error during rendering. However, these
* will frequently lead to an avalanche of error messages. Therefore, error
* messages are managed by this interface, ensuring that they are displayed not
* more than once per frame.
*
* Error functions can always be counted on to return FAIL, like ErrorLog().
*/
#include "Supermodel.h"
namespace Legacy3D {
// Error bit flags (must not conflict)
#define ERROR_LOCAL_VERTEX_OVERFLOW 0x1
#define ERROR_UNABLE_TO_CACHE_MODEL 0x2
// Overflow in the local vertex buffer, which holds one model
bool CLegacy3D::ErrorLocalVertexOverflow(void)
{
if ((errorMsgFlags&ERROR_LOCAL_VERTEX_OVERFLOW))
return FAIL;
errorMsgFlags |= ERROR_LOCAL_VERTEX_OVERFLOW;
return ErrorLog("Overflow in local vertex buffer!");
}
// Model could not be cached, even after dumping display list and re-caching
bool CLegacy3D::ErrorUnableToCacheModel(UINT32 modelAddr)
{
if ((errorMsgFlags&ERROR_UNABLE_TO_CACHE_MODEL))
return FAIL;
errorMsgFlags |= ERROR_UNABLE_TO_CACHE_MODEL;
return ErrorLog("Encountered a model that was too large to cache (at %08X)!", modelAddr);
}
// Call this every frame to clear the error flag, allowing errors to be printed
void CLegacy3D::ClearErrors(void)
{
errorMsgFlags = 0;
}
} // Legacy3D
File diff suppressed because it is too large. Load diff
+524
View File
@@ -0,0 +1,524 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Legacy3D.h
*
* Header file defining the CLegacy3D class: Supermodel's original OpenGL
* Real3D graphics engine.
*/
#ifndef INCLUDED_LEGACY3D_H
#define INCLUDED_LEGACY3D_H
#include "Graphics/IRender3D.h"
#include "Pkgs/glew.h"
#include "Util/NewConfig.h"
namespace Legacy3D {
/******************************************************************************
Internal Definitions and Data Structures
******************************************************************************/
// Model caches sort models by alpha (translucency) state
enum POLY_STATE
{
POLY_STATE_NORMAL = 0,
POLY_STATE_ALPHA
};
struct Vertex
{
GLfloat x,y,z; // vertex
GLfloat n[3]; // normal X, Y, Z
GLfloat u,v; // texture U, V coordinates (in texels, relative to selected texture)
GLfloat intensity; // shading intensity (if using per-vertex fixed shading)
};
struct Poly
{
Vertex Vert[4];
GLfloat n[3]; // polygon normal (used for backface culling and flat shading)
POLY_STATE state; // alpha or normal?
unsigned numVerts; // triangle (3) or quad (4)
const UINT32 *header; // pointer to Real3D 7-word polygon header
};
/*
* VBORef:
*
* Reference to model polygons stored in a VBO. Each reference has two sets of
* vertices: normal and alpha. Copies of the model with different texture
* offsets applied are searchable via the linked list of texture offset states.
*
* Technically, a model may contain a mix of layered and non-layered polygons
* but we can't support that level of granularity in the current engine. The
* useStencil flag is set only when all polygons in a model are layered, and
* also may be set when we detect that a polygon is likely to be used as a
* shadow. On the actual hardware, these are not stenciled but are most likely
* implemented with stipple masks. We cheat here to make shadows look nicer.
*/
struct VBORef
{
unsigned index[2]; // index of model polygons in VBO
unsigned numVerts[2]; // number of vertices
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
VBORef *nextTextureOffsetState; // linked list of models with different texture offset states
uint16_t textureOffsetState; // texture offset data for this model
bool useStencil; // whether to draw with stencil mask ("layered" polygons)
CTextureRefs texRefs; // unique texture references contained in this model
/*
* Clear():
*
* Clears the VBORef by setting all fields to 0 and clearing the texture
* references.
*/
inline void Clear(void)
{
texRefs.Clear();
lutIdx = 0;
textureOffsetState = 0;
nextTextureOffsetState = NULL;
useStencil = false;
for (int i = 0; i < 2; i++)
{
index[i] = 0;
numVerts[i] = 0;
}
}
};
// Display list items: model instances and viewport settings
struct DisplayList
{
// Viewport instance data
struct ViewportInstance
{
GLfloat projectionMatrix[4*4]; // projection matrix
GLfloat lightingParams[6]; // lighting parameters (see RenderViewport() and vertex shader)
GLfloat spotEllipse[4]; // spotlight ellipse (see RenderViewport())
GLfloat spotRange[2]; // Z range
GLfloat spotColor[3]; // color
GLfloat fogParams[5]; // fog parameters (...)
GLint x, y; // viewport coordinates (scaled and in OpenGL format)
GLint width, height; // viewport dimensions (scaled for display surface size)
};
// Model instance data
struct ModelInstance
{
GLfloat modelViewMatrix[4*4]; // model-view matrix
unsigned index; // index in VBO
unsigned numVerts; // number of vertices
GLint frontFace; // GL_CW (default), GL_CCW, or -GL_CW to indicate no culling
bool useStencil; // draw with stencil testing
};
bool isViewport; // if true, this is a viewport node
union
{
ModelInstance Model;
ViewportInstance Viewport;
} Data;
DisplayList *next; // next display list item with the same state (alpha or non-alpha)
};
/*
* ModelCache:
*
* A model cache tracks all models in a particular region (ie., VROM or polygon
* RAM). It contains a look-up table to quickly obtain VBO indices. Be careful
* when accessing the LUT, there are some special cases.
*
* If the model cache is marked dynamic, cached models may not necessarily be
* retained. Clearing the model cache is also much faster. The LUT entry for
* the last model cached will be valid, but because the LUT may not be
* cleared, one cannot assume a model exists because there is a LUT entry
* pointing to it. Always use NeedToCache() to determine whether caching is
* necessary before reading the LUT!
*/
struct ModelCache
{
// Cache type
bool dynamic;
// Vertex buffer object
unsigned vboMaxOffset; // size of VBO (in bytes)
unsigned vboCurOffset; // current offset in VBO (in bytes)
GLuint vboID; // OpenGL VBO handle
// Local vertex buffers (enough for a single model)
unsigned maxVertIdx; // size of each local vertex buffer (in vertices)
unsigned curVertIdx[2]; // current vertex index (in vertices)
GLfloat *verts[2];
// Array of cached models
unsigned maxModels; // maximum number of models
unsigned numModels; // current number stored
VBORef *Models;
/*
* Look-Up Table:
*
* Can be accessed directly with a LUT index to determine the model index.
* However, it should not be used to determine whether a model needs to be
* cached. Use NeedToCache() instead. A valid index, for example, may still
* have to be re-cached if the model cache is dynamic (polygon RAM).
*/
unsigned lutSize; // number of elements in LUT
INT16 *lut; // stores indices into Models[] or -1 if not yet cached
// Display list
unsigned maxListSize; // maximum number of display list items
unsigned listSize; // number of items in display list
DisplayList *List; // holds all display list items
DisplayList *ListHead[2]; // heads of linked lists for each state
DisplayList *ListTail[2]; // current tail node for each state
};
struct TexSheet
{
unsigned sheetNum;
unsigned mapNum;
unsigned xOffset;
unsigned yOffset;
/*
* Texture Format Buffer
*
* Records the format that a texture (at a given location within the
* texture sheet) is currently stored in. A negative value indicates the
* texture has not been accessed and converted yet and non-negative values
* correspond to the texture format bits in the polygon headers. They can
* be used to determine whether a texture needs to be updated.
*/
int texWidth[2048/32][2048/32];
int texHeight[2048/32][2048/32];
INT8 texFormat[2048/32][2048/32];
};
/******************************************************************************
CLegacy3D Classes
******************************************************************************/
/*
* CLegacy3D:
*
* 3D renderer. Lots of work to do here :)
*/
class CLegacy3D: public IRender3D
{
friend class CTextureRefs;
public:
/*
* RenderFrame(void):
*
* Renders the complete scene database. Must be called between BeginFrame() and
* EndFrame(). This function traverses the scene database and builds up display
* lists.
*/
void RenderFrame(void);
/*
* BeginFrame(void):
*
* Prepare to render a new frame. Must be called once per frame prior to
* drawing anything.
*/
void BeginFrame(void);
/*
* EndFrame(void):
*
* Signals the end of rendering for this frame. Must be called last during
* the frame.
*/
void EndFrame(void);
/*
* UploadTextures(x, y, width, height):
*
* Signals that a portion of texture RAM has been updated.
*
* Parameters:
* x X position within texture RAM.
* y Y position within texture RAM.
* width Width of texture data in texels.
* height Height.
*/
void UploadTextures(unsigned level, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* AttachMemory(cullingRAMLoPtr, cullingRAMHiPtr, polyRAMPtr, vromPtr,
* textureRAMPtr):
*
* Attaches RAM and ROM areas. This must be done prior to any rendering
* otherwise the program may crash with an access violation.
*
* Parameters:
* cullingRAMLoPtr Pointer to low culling RAM (4 MB).
* cullingRAMHiPtr Pointer to high culling RAM (1 MB).
* polyRAMPtr Pointer to polygon RAM (4 MB).
* vromPtr Pointer to video ROM (64 MB).
* textureRAMPtr Pointer to texture RAM (8 MB).
*/
void AttachMemory(const UINT32 *cullingRAMLoPtr,
const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr,
const UINT32 *vromPtr, const UINT16 *textureRAMPtr);
/*
* SetStepping(stepping):
*
* Sets the Model 3 hardware stepping, which also determines the Real3D
* functionality. The default is Step 1.0. This should be called prior to
* any other emulation functions and after Init().
*
* Parameters:
* stepping 0x10 for Step 1.0, 0x15 for Step 1.5, 0x20 for Step 2.0, or
* 0x21 for Step 2.1. Anything else defaults to 1.0.
*/
void SetStepping(int stepping);
/*
* Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes):
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
* other objects that want to use it).
*
* External shader files are loaded according to configuration settings.
*
* Parameters:
* xOffset X offset of the viewable area within OpenGL display
* surface, in pixels.
* yOffset Y offset.
* xRes Horizontal resolution of the viewable area.
* yRes Vertical resolution.
* totalXRes Horizontal resolution of the complete display area.
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
/*
* SetSunClamp(bool enable);
*
* Sets or unsets the clamped light model
*
* Parameters:
* enable Set clamp mode
*/
void SetSunClamp(bool enable);
/*
* SetSignedShade(bool enable);
*
* Sets the sign-ness of fixed shading value
*
* Parameters:
* enable Fixed shading is expressed as signed value
*/
void SetSignedShade(bool enable);
/*
* CLegacy3D(void):
* ~CLegacy3D(void):
*
* Parameters:
* config Configuration object.
*
* Constructor and destructor.
*/
CLegacy3D(const Util::Config::Node &config);
~CLegacy3D(void);
private:
/*
* Private Members
*/
// Real3D address translation
const UINT32 *TranslateCullingAddress(UINT32 addr);
const UINT32 *TranslateModelAddress(UINT32 addr);
// Model caching and display list management
void DrawDisplayList(ModelCache *Cache, POLY_STATE state);
bool AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model);
void ClearDisplayList(ModelCache *Cache);
int GetTextureBaseX(const Poly *P) const;
int GetTextureBaseY(const Poly *P) const;
bool InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
struct VBORef *BeginModel(ModelCache *cache);
void EndModel(ModelCache *cache, struct VBORef *Model, int lutIdx, UINT16 textureOffsetState, bool useStencil);
struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 textureOffsetState, const UINT32 *data);
struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 textureOffsetState);
void ClearModelCache(ModelCache *cache);
bool CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, bool isDynamic);
void DestroyModelCache(ModelCache *cache);
// Texture management
void DecodeTexture(int format, int x, int y, int width, int height);
// Matrix stack
void MultMatrix(UINT32 matrixOffset);
void InitMatrixStack(UINT32 matrixBaseAddr);
// Scene database traversal
bool DrawModel(UINT32 modelAddr);
void DescendCullingNode(UINT32 addr);
void DescendPointerList(UINT32 addr);
void DescendNodePtr(UINT32 nodeAddr);
void RenderViewport(UINT32 addr, int pri, bool wideScreen);
// In-frame error reporting
bool ErrorLocalVertexOverflow(void);
bool ErrorUnableToCacheModel(UINT32 modelAddr);
void ClearErrors(void);
/*
* Data
*/
const Util::Config::Node &m_config;
#ifdef DEBUG
// Debug
int m_debugHighlightPolyHeaderIdx = -1;
uint32_t m_debugHighlightPolyHeaderMask = 0;
int m_debugHighlightCullingNodeIdx = -1;
uint32_t m_debugHighlightCullingNodeMask = 0;
bool m_debugHighlightAll = false;
#endif
// Stepping
int step;
int offset; // offset to subtract for words 3 and higher of culling nodes
GLfloat vertexFactor; // fixed-point conversion factor for vertices
// Memory (passed from outside)
const UINT32 *cullingRAMLo; // 4 MB
const UINT32 *cullingRAMHi; // 1 MB
const UINT32 *polyRAM; // 4 MB
const UINT32 *vrom; // 64 MB
const UINT16 *textureRAM; // 8 MB
// Error reporting
unsigned errorMsgFlags; // tracks which errors have been printed this frame
// Real3D Base Matrix Pointer
const float *matrixBasePtr;
// Current viewport parameters (updated as viewports are traversed)
GLfloat lightingParams[6];
GLfloat fogParams[5];
GLfloat spotEllipse[4];
GLfloat spotRange[2];
GLfloat spotColor[3];
GLint viewportX, viewportY;
GLint viewportWidth, viewportHeight;
// Scene graph processing
int listDepth; // how many lists have we recursed into
int stackDepth; // for debugging and error handling purposes
struct TextureOffset
{
int x; // x offset
int y; // y offset (wraps within 2048x1023 texel bank)
int switchBank; // 0: use bank from polygon header, 1024: swap banks
uint16_t state; // x, y, and bank states compromise a unique key
TextureOffset(uint32_t data)
: x(32 * ((data >> 7) & 0x7F)),
y(32 * (data & 0x7F)),
switchBank((data & 0x4000) >> 4),
state(data & 0x7FFF)
{}
TextureOffset()
: x(0),
y(0),
switchBank(false),
state(0)
{}
} m_textureOffset;
UINT32 m_colorTableAddr = 0x400; // address of color table in polygon RAM
// Resolution and scaling factors (to support resolutions higher than 496x384) and offsets
GLfloat xRatio, yRatio;
unsigned xOffs, yOffs;
unsigned totalXRes, totalYRes;
// Texture details
static int defaultFmtToTexSheetNum[8]; // default mapping from Model3 texture format to texture sheet
unsigned numTexMaps; // total number of texture maps
GLuint texMapIDs[9]; // GL texture IDs of texture maps
unsigned numTexSheets; // total number of texture sheets
TexSheet *texSheets; // texture sheet objects
TexSheet *fmtToTexSheet[8]; // final mapping from Model3 texture format to texture sheet
// Shader programs and input data locations
GLuint shaderProgram; // shader program object
GLuint vertexShader; // vertex shader handle
GLuint fragmentShader; // fragment shader
GLint textureMapLoc; // location of "textureMap" uniform (if available)
GLint textureMapLocs[8]; // location of "textureMap[0-7]" uniforms (if available)
GLint modelViewMatrixLoc; // uniform
GLint projectionMatrixLoc; // uniform
GLint lightingLoc; // uniform
GLint mapSizeLoc; // uniform
GLint spotEllipseLoc; // uniform
GLint spotRangeLoc; // uniform
GLint spotColorLoc; // uniform
GLint subTextureLoc; // attribute
GLint texParamsLoc; // attribute
GLint texFormatLoc; // attribute
GLint texMapLoc; // attribute
GLint transLevelLoc; // attribute
GLint lightEnableLoc; // attribute
GLint specularLoc; // attribute
GLint shininessLoc; // attribute
GLint fogIntensityLoc; // attribute
// Model caching
ModelCache VROMCache; // VROM (static) models
ModelCache PolyCache; // polygon RAM (dynamic) models
/*
* Texture Decode Buffer
*
* Textures are decoded and copied from texture RAM into this temporary buffer
* before being uploaded. Dimensions are 512x512.
*/
GLfloat *textureBuffer; // RGBA8 format
};
} // Legacy3D
#endif // INCLUDED_LEGACY3D_H
File diff suppressed because it is too large. Load diff
+2
View File
@@ -0,0 +1,2 @@
Shader source files go here. Completed versions of the shader files should be
copied into Src/Graphics/Shaders3D.h and Src/Graphics/Shaders2D.h.
+194
View File
@@ -0,0 +1,194 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Fragment.glsl
*
* Fragment shader for 3D rendering.
*/
#version 120
// Global uniforms
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform float mapSize; // texture map size (2048,4096,6144 etc)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTexFormat; // T1RGB5 contour texture (if > 0)
varying float fsTexMap; // texture map number
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
varying vec3 fsLightIntensity; // lighting intensity
varying float fsSpecularTerm; // specular highlight
varying float fsFogFactor; // fog factor
varying float fsViewZ; // Z distance to fragment from viewpoint at origin
/*
* WrapTexelCoords():
*
* Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture
* sheet, taking into account wrapping behavior.
*
* Computing normalized OpenGL texture coordinates (0 to 1) within the
* Real3D texture sheet:
*
* If the texture is not mirrored, we simply have to clamp the
* coordinates to fit within the texture dimensions, add the texture
* X, Y position to select the appropriate one, and normalize by 2048
* (the dimensions of the Real3D texture sheet).
*
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
*
* If mirroring is enabled, textures are mirrored every odd multiple of
* the original texture. To detect whether we are in an odd multiple,
* simply divide the coordinate by the texture dimension and check
* whether the result is odd. Then, clamp the coordinates as before but
* subtract from the last texel to mirror them:
*
* = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
* where M is 1.0 if the texture must be mirrored.
*
* As an optimization, this function computes TWO texture coordinates
* simultaneously. The first is texCoord.xy, the second is in .zw. The other
* parameters must have .xy = .zw.
*/
vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)
{
vec4 clampedCoord, mirror, glTexCoord;
clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size
mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / mapSize;
return glTexCoord;
}
/*
* main():
*
* Fragment shader entry point.
*/
void main(void)
{
vec4 uv_top, uv_bot, c[4];
vec2 r;
vec4 fragColor;
vec2 ellipse;
vec3 lightIntensity;
float insideSpot;
int x;
// Get polygon color for untextured polygons (textured polygons will overwrite)
if (fsTexParams.x < 0.5)
fragColor = gl_Color;
else
// Textured polygons: set fragment color to texel value
{
/*
* Bilinear Filtering
*
* In order to get this working on ATI, the number of operations is
* reduced by putting everything into vec4s. uv_top holds the UV
* coordinates for the top two texels (.xy=left, .zw=right) and uv_bot
* is for the lower two.
*/
// Compute fractional blending factor, r, and lower left corner of texel 0
uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel
r = uv_bot.xy-floor(uv_bot.xy); // fractional part
uv_bot.xy = floor(uv_bot.xy); // integral part
// Compute texel coordinates
uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)
uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors
uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);
// Compute the properly wrapped texel coordinates
uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
// Fetch the texels
c[0]=texture2D(textureMap,uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap,uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap,uv_top.xy); // top-left
c[3]=texture2D(textureMap,uv_top.zw); // top-right
// Interpolate texels and blend result with material color to determine final (unlit) fragment color
// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);
// Faster method:
c[0] += (c[1]-c[0])*r.s; // 2 alu
c[2] += (c[3]-c[2])*r.s; // 2 alu
fragColor = c[0]+(c[2]-c[0])*r.t; //2 alu
/*
* T1RGB5:
*
* The transparency bit determines whether to discard pixels (if set).
* What is unknown is how this bit behaves when interpolated. OpenGL
* processes it as an alpha value, so it might concievably be blended
* with neighbors. Here, an arbitrary threshold is chosen.
*
* To-do: blending could probably enabled and this would work even
* better with a hard threshold.
*
* Countour processing also seems to be enabled for RGBA4 textures.
* When the alpha value is 0.0 (or close), pixels are discarded
* entirely.
*/
if (fsTexParams.y > 0.5) // contour processing enabled
{
if (fragColor.a < 0.01) // discard anything with alpha == 0
discard;
}
// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency
if (fsTexFormat < 0.5) // contour (T1RGB5) texture
fragColor.a = 1.0;
}
// Compute spotlight and apply lighting
ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;
insideSpot = dot(ellipse,ellipse);
if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))
lightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;
else
lightIntensity = fsLightIntensity;
fragColor.rgb *= lightIntensity;
fragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);
// Translucency (modulates existing alpha channel for RGBA4 texels)
fragColor.a *= fsTransLevel;
// Apply fog
fragColor.rgb = mix(fragColor.rgb, gl_Fog.color.rgb, fsFogFactor);
// Store final color
gl_FragColor = fragColor;
}
@@ -0,0 +1,42 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Fragment2D.glsl
*
* Fragment shader for 2D tilemap rendering.
*/
#version 120
// Global uniforms
uniform sampler2D textureMap; // 512x512 layer surface
/*
* main():
*
* Fragment shader entry point.
*/
void main(void)
{
gl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);
}
@@ -0,0 +1,142 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Fragment_NoSpotlight.glsl
*
* Fragment shader for 3D rendering. Spotlight effect removed. Fixes fragment
* shader link errors on older ATI Radeon GPUs.
*
* To load external fragment shaders, use the -frag-shader=<file> option when
* starting Supermodel.
*/
#version 120
// Global uniforms
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform float mapSize; // texture map size (2048,4096,6144 etc)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0)
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
varying vec3 fsLightIntensity; // lighting intensity
varying float fsFogFactor; // fog factor
varying float fsViewZ; // Z distance to fragment from viewpoint at origin
/*
* WrapTexelCoords():
*
* Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture
* sheet, taking into account wrapping behavior.
*
* Computing normalized OpenGL texture coordinates (0 to 1) within the
* Real3D texture sheet:
*
* If the texture is not mirrored, we simply have to clamp the
* coordinates to fit within the texture dimensions, add the texture
* X, Y position to select the appropriate one, and normalize by 2048
* (the dimensions of the Real3D texture sheet).
*
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
*
* If mirroring is enabled, textures are mirrored every odd multiple of
* the original texture. To detect whether we are in an odd multiple,
* simply divide the coordinate by the texture dimension and check
* whether the result is odd. Then, clamp the coordinates as before but
* subtract from the last texel to mirror them:
*
* = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
* where M is 1.0 if the texture must be mirrored.
*
* As an optimization, this function computes TWO texture coordinates
* simultaneously. The first is texCoord.xy, the second is in .zw. The other
* parameters must have .xy = .zw.
*/
vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)
{
vec4 clampedCoord, mirror, glTexCoord;
clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size
mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / mapSize;
/*
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / mapSize;
*/
return glTexCoord;
}
/*
* main():
*
* Fragment shader entry point.
*/
void main(void)
{
vec4 uv_top, uv_bot, c[4];
vec2 r;
vec4 fragColor;
vec2 ellipse;
vec3 lightIntensity;
float insideSpot;
// Get polygon color for untextured polygons (textured polygons will overwrite)
if (fsTexParams.x < 0.5)
fragColor = gl_Color;
else
// Textured polygons: set fragment color to texel value
{
fragColor = texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw/2.0)/mapSize);
//fragColor += texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw)/mapSize);
}
// Compute spotlight and apply lighting
ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;
insideSpot = dot(ellipse,ellipse);
if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))
lightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;
else
lightIntensity = fsLightIntensity;
fragColor.rgb *= lightIntensity;
// Translucency (modulates existing alpha channel for RGBA4 texels)
fragColor.a *= fsTransLevel;
// Apply fog under the control of fog factor setting from polygon header
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );
// Store final color
gl_FragColor = fragColor;
}
Loaded 100 of 354 files, more files were not shown because too many files have changed in this diff. Show more