mirror of https://github.com/Treeki/WindEmu.git
initial commit
This commit is contained in:
commit
1d6e77ced8
|
@ -0,0 +1 @@
|
||||||
|
*.pro.user*
|
|
@ -0,0 +1,373 @@
|
||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,53 @@
|
||||||
|
WindEmu is an attempt to emulate the Psion Series 5mx (or as internally called, "Windermere"). I don't think anyone's done this before... or if they have, I can't find evidence online!
|
||||||
|
|
||||||
|
- Platform-independent core emulation library written in C/C++
|
||||||
|
- Qt5 front-end (currently quite barebones...)
|
||||||
|
- Very experimental
|
||||||
|
- Boots from the Psion 5mx Pro's Sys$rom.bin
|
||||||
|
|
||||||
|
Hardware features:
|
||||||
|
|
||||||
|
- ✅ LCD: partially implemented
|
||||||
|
- ✅ Keyboard: implemented
|
||||||
|
- ❌ Touch panel: not implemented
|
||||||
|
- ❌ Audio: not implemented
|
||||||
|
- ❌ Serial/UART support: stubbed out
|
||||||
|
- ❌ ETNA (CompactFlash): not implemented
|
||||||
|
- ✅ RTC: implemented
|
||||||
|
- ❌ RTC alarm: not implemented
|
||||||
|
- ❌ Standby mode: not implemented
|
||||||
|
|
||||||
|
Known issues:
|
||||||
|
|
||||||
|
- ROM path is hardcoded into WindQt/main.cpp right now
|
||||||
|
- Memory protection is not enforced
|
||||||
|
- Memory errors do not result in an Abort exception but instead make the emulator freak out
|
||||||
|
- Some keys do not work properly
|
||||||
|
- State is not saved (just like a real Psion :p)
|
||||||
|
- LCD controller is almost entirely unimplemented aside from the very basics to display the framebuffer
|
||||||
|
- EPOC misbehaves massively with memory banks larger than 0x800000 (may be an OS design flaw? need to confirm)
|
||||||
|
- 4bpp display mode does not decode correctly
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
---------
|
||||||
|
|
||||||
|
The Psion-specific code is copyright (c) 2019 Ash Wolf.
|
||||||
|
|
||||||
|
The ARM emulation core is a modified version of the one used in [mGBA](https://github.com/mgba-emu/mgba) by endrift.
|
||||||
|
|
||||||
|
WindEmu is available under the Mozilla Public License 2.0.
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
|
||||||
|
Special thanks to [PsiLinux/OpenPsion](http://linux-7110.sourceforge.net/index.shtml) for providing an avenue to learn about the hardware definitions (registers, etc).
|
||||||
|
|
||||||
|
The EPOC C++ SDK is available here: https://web.archive.org/web/20071010101808/http://www.psionteklogix.com/teknet/pdk/netpad-pdk/epoc_downloads.htm
|
||||||
|
|
||||||
|
The ARM variant used in the 5mx is documented here: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0033d/DDI0033D_710a_prelim_ds.pdf
|
||||||
|
|
||||||
|
The datasheet for the CL-PS7110 SoC used in the Series 5 (_not_ the 5mx) is available here: https://www.igorkov.org/revo/datasheets/CL-PS7110.pdf - while not identical to Windermere, some components operate in similar fashion.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2019-12-18T16:18:09
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT -= core gui
|
||||||
|
|
||||||
|
TARGET = WindCore
|
||||||
|
TEMPLATE = lib
|
||||||
|
CONFIG += staticlib
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
wind.cpp \
|
||||||
|
isa-arm.c \
|
||||||
|
decoder.c \
|
||||||
|
decoder-arm.c \
|
||||||
|
arm.c \
|
||||||
|
emu.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
wind_hw.h \
|
||||||
|
wind.h \
|
||||||
|
macros.h \
|
||||||
|
isa-inlines.h \
|
||||||
|
isa-arm.h \
|
||||||
|
emitter-inlines.h \
|
||||||
|
emitter-arm.h \
|
||||||
|
decoder.h \
|
||||||
|
decoder-inlines.h \
|
||||||
|
common.h \
|
||||||
|
arm.h \
|
||||||
|
emu.h
|
||||||
|
unix {
|
||||||
|
target.path = /usr/lib
|
||||||
|
INSTALLS += target
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "arm.h"
|
||||||
|
#include "isa-arm.h"
|
||||||
|
#include "isa-inlines.h"
|
||||||
|
|
||||||
|
static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode);
|
||||||
|
|
||||||
|
void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
|
||||||
|
if (mode == cpu->privilegeMode) {
|
||||||
|
// Not switching modes after all
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RegisterBank newBank = _ARMSelectBank(mode);
|
||||||
|
enum RegisterBank oldBank = _ARMSelectBank(cpu->privilegeMode);
|
||||||
|
if (newBank != oldBank) {
|
||||||
|
// Switch banked registers
|
||||||
|
if (mode == MODE_FIQ || cpu->privilegeMode == MODE_FIQ) {
|
||||||
|
int oldFIQBank = oldBank == BANK_FIQ;
|
||||||
|
int newFIQBank = newBank == BANK_FIQ;
|
||||||
|
cpu->bankedRegisters[oldFIQBank][2] = cpu->gprs[8];
|
||||||
|
cpu->bankedRegisters[oldFIQBank][3] = cpu->gprs[9];
|
||||||
|
cpu->bankedRegisters[oldFIQBank][4] = cpu->gprs[10];
|
||||||
|
cpu->bankedRegisters[oldFIQBank][5] = cpu->gprs[11];
|
||||||
|
cpu->bankedRegisters[oldFIQBank][6] = cpu->gprs[12];
|
||||||
|
cpu->gprs[8] = cpu->bankedRegisters[newFIQBank][2];
|
||||||
|
cpu->gprs[9] = cpu->bankedRegisters[newFIQBank][3];
|
||||||
|
cpu->gprs[10] = cpu->bankedRegisters[newFIQBank][4];
|
||||||
|
cpu->gprs[11] = cpu->bankedRegisters[newFIQBank][5];
|
||||||
|
cpu->gprs[12] = cpu->bankedRegisters[newFIQBank][6];
|
||||||
|
}
|
||||||
|
cpu->bankedRegisters[oldBank][0] = cpu->gprs[ARM_SP];
|
||||||
|
cpu->bankedRegisters[oldBank][1] = cpu->gprs[ARM_LR];
|
||||||
|
cpu->gprs[ARM_SP] = cpu->bankedRegisters[newBank][0];
|
||||||
|
cpu->gprs[ARM_LR] = cpu->bankedRegisters[newBank][1];
|
||||||
|
|
||||||
|
cpu->bankedSPSRs[oldBank] = cpu->spsr.packed;
|
||||||
|
cpu->spsr.packed = cpu->bankedSPSRs[newBank];
|
||||||
|
}
|
||||||
|
cpu->privilegeMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case MODE_USER:
|
||||||
|
case MODE_SYSTEM:
|
||||||
|
// No banked registers
|
||||||
|
return BANK_NONE;
|
||||||
|
case MODE_FIQ:
|
||||||
|
return BANK_FIQ;
|
||||||
|
case MODE_IRQ:
|
||||||
|
return BANK_IRQ;
|
||||||
|
case MODE_SUPERVISOR:
|
||||||
|
return BANK_SUPERVISOR;
|
||||||
|
case MODE_ABORT:
|
||||||
|
return BANK_ABORT;
|
||||||
|
case MODE_UNDEFINED:
|
||||||
|
return BANK_UNDEFINED;
|
||||||
|
default:
|
||||||
|
// This should be unreached
|
||||||
|
return BANK_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMReset(struct ARMCore* cpu) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 16; ++i) {
|
||||||
|
cpu->gprs[i] = 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 6; ++i) {
|
||||||
|
cpu->bankedRegisters[i][0] = 0;
|
||||||
|
cpu->bankedRegisters[i][1] = 0;
|
||||||
|
cpu->bankedRegisters[i][2] = 0;
|
||||||
|
cpu->bankedRegisters[i][3] = 0;
|
||||||
|
cpu->bankedRegisters[i][4] = 0;
|
||||||
|
cpu->bankedRegisters[i][5] = 0;
|
||||||
|
cpu->bankedRegisters[i][6] = 0;
|
||||||
|
cpu->bankedSPSRs[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu->privilegeMode = MODE_SYSTEM;
|
||||||
|
cpu->cpsr.packed = MODE_SYSTEM;
|
||||||
|
cpu->spsr.packed = 0;
|
||||||
|
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = 0;
|
||||||
|
|
||||||
|
ARMWritePC(cpu);
|
||||||
|
|
||||||
|
cpu->cycles = 0;
|
||||||
|
cpu->nextEvent = 0;
|
||||||
|
cpu->halted = 0;
|
||||||
|
|
||||||
|
cpu->irqh.reset(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRaiseFIQ(struct ARMCore* cpu) {
|
||||||
|
if (cpu->cpsr.f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
union PSR cpsr = cpu->cpsr;
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_FIQ);
|
||||||
|
cpu->cpsr.priv = MODE_FIQ;
|
||||||
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC];
|
||||||
|
cpu->gprs[ARM_PC] = BASE_FIQ;
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
|
cpu->spsr = cpsr;
|
||||||
|
cpu->cpsr.f = 1;
|
||||||
|
cpu->cpsr.i = 1;
|
||||||
|
cpu->halted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRaiseIRQ(struct ARMCore* cpu) {
|
||||||
|
if (cpu->cpsr.i) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
union PSR cpsr = cpu->cpsr;
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_IRQ);
|
||||||
|
cpu->cpsr.priv = MODE_IRQ;
|
||||||
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC];
|
||||||
|
cpu->gprs[ARM_PC] = BASE_IRQ;
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
|
cpu->spsr = cpsr;
|
||||||
|
cpu->cpsr.i = 1;
|
||||||
|
cpu->halted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRaiseSWI(struct ARMCore* cpu) {
|
||||||
|
union PSR cpsr = cpu->cpsr;
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
|
||||||
|
cpu->cpsr.priv = MODE_SUPERVISOR;
|
||||||
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4;
|
||||||
|
cpu->gprs[ARM_PC] = BASE_SWI;
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
|
cpu->spsr = cpsr;
|
||||||
|
cpu->cpsr.i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRaiseUndefined(struct ARMCore* cpu) {
|
||||||
|
union PSR cpsr = cpu->cpsr;
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_UNDEFINED);
|
||||||
|
cpu->cpsr.priv = MODE_UNDEFINED;
|
||||||
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4;
|
||||||
|
cpu->gprs[ARM_PC] = BASE_UNDEF;
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
|
cpu->spsr = cpsr;
|
||||||
|
cpu->cpsr.i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ARMStep(struct ARMCore* cpu) {
|
||||||
|
uint32_t opcode = cpu->prefetch[0];
|
||||||
|
cpu->prefetch[0] = cpu->prefetch[1];
|
||||||
|
cpu->gprs[ARM_PC] += 4;
|
||||||
|
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
||||||
|
|
||||||
|
unsigned condition = opcode >> 28;
|
||||||
|
if (condition != 0xE) {
|
||||||
|
bool conditionMet = false;
|
||||||
|
switch (condition) {
|
||||||
|
case 0x0:
|
||||||
|
conditionMet = ARM_COND_EQ;
|
||||||
|
break;
|
||||||
|
case 0x1:
|
||||||
|
conditionMet = ARM_COND_NE;
|
||||||
|
break;
|
||||||
|
case 0x2:
|
||||||
|
conditionMet = ARM_COND_CS;
|
||||||
|
break;
|
||||||
|
case 0x3:
|
||||||
|
conditionMet = ARM_COND_CC;
|
||||||
|
break;
|
||||||
|
case 0x4:
|
||||||
|
conditionMet = ARM_COND_MI;
|
||||||
|
break;
|
||||||
|
case 0x5:
|
||||||
|
conditionMet = ARM_COND_PL;
|
||||||
|
break;
|
||||||
|
case 0x6:
|
||||||
|
conditionMet = ARM_COND_VS;
|
||||||
|
break;
|
||||||
|
case 0x7:
|
||||||
|
conditionMet = ARM_COND_VC;
|
||||||
|
break;
|
||||||
|
case 0x8:
|
||||||
|
conditionMet = ARM_COND_HI;
|
||||||
|
break;
|
||||||
|
case 0x9:
|
||||||
|
conditionMet = ARM_COND_LS;
|
||||||
|
break;
|
||||||
|
case 0xA:
|
||||||
|
conditionMet = ARM_COND_GE;
|
||||||
|
break;
|
||||||
|
case 0xB:
|
||||||
|
conditionMet = ARM_COND_LT;
|
||||||
|
break;
|
||||||
|
case 0xC:
|
||||||
|
conditionMet = ARM_COND_GT;
|
||||||
|
break;
|
||||||
|
case 0xD:
|
||||||
|
conditionMet = ARM_COND_LE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!conditionMet) {
|
||||||
|
cpu->cycles += ARM_PREFETCH_CYCLES;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
|
||||||
|
instruction(cpu, opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRun(struct ARMCore* cpu) {
|
||||||
|
ARMStep(cpu);
|
||||||
|
if (cpu->cycles >= cpu->nextEvent) {
|
||||||
|
cpu->irqh.processEvents(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRunLoop(struct ARMCore* cpu) {
|
||||||
|
while (cpu->cycles < cpu->nextEvent) {
|
||||||
|
ARMStep(cpu);
|
||||||
|
}
|
||||||
|
cpu->irqh.processEvents(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMRunFake(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
|
cpu->gprs[ARM_PC] -= 4;
|
||||||
|
cpu->prefetch[1] = cpu->prefetch[0];
|
||||||
|
cpu->prefetch[0] = opcode;
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef ARM_H
|
||||||
|
#define ARM_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
// #include <mgba/core/cpu.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARM_SP = 13,
|
||||||
|
ARM_LR = 14,
|
||||||
|
ARM_PC = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PrivilegeMode {
|
||||||
|
MODE_USER = 0x10,
|
||||||
|
MODE_FIQ = 0x11,
|
||||||
|
MODE_IRQ = 0x12,
|
||||||
|
MODE_SUPERVISOR = 0x13,
|
||||||
|
MODE_ABORT = 0x17,
|
||||||
|
MODE_UNDEFINED = 0x1B,
|
||||||
|
MODE_SYSTEM = 0x1F
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ExecutionVector {
|
||||||
|
BASE_RESET = 0x00000000,
|
||||||
|
BASE_UNDEF = 0x00000004,
|
||||||
|
BASE_SWI = 0x00000008,
|
||||||
|
BASE_PABT = 0x0000000C,
|
||||||
|
BASE_DABT = 0x00000010,
|
||||||
|
BASE_IRQ = 0x00000018,
|
||||||
|
BASE_FIQ = 0x0000001C
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RegisterBank {
|
||||||
|
BANK_NONE = 0,
|
||||||
|
BANK_FIQ = 1,
|
||||||
|
BANK_IRQ = 2,
|
||||||
|
BANK_SUPERVISOR = 3,
|
||||||
|
BANK_ABORT = 4,
|
||||||
|
BANK_UNDEFINED = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LSMDirection {
|
||||||
|
LSM_B = 1,
|
||||||
|
LSM_D = 2,
|
||||||
|
LSM_IA = 0,
|
||||||
|
LSM_IB = 1,
|
||||||
|
LSM_DA = 2,
|
||||||
|
LSM_DB = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ARMCore;
|
||||||
|
|
||||||
|
union PSR {
|
||||||
|
struct {
|
||||||
|
#if defined(__POWERPC__) || defined(__PPC__)
|
||||||
|
unsigned n : 1;
|
||||||
|
unsigned z : 1;
|
||||||
|
unsigned c : 1;
|
||||||
|
unsigned v : 1;
|
||||||
|
unsigned unused : 20;
|
||||||
|
unsigned i : 1;
|
||||||
|
unsigned f : 1;
|
||||||
|
unsigned t : 1;
|
||||||
|
unsigned priv : 5;
|
||||||
|
#else
|
||||||
|
unsigned priv : 5;
|
||||||
|
unsigned t : 1;
|
||||||
|
unsigned f : 1;
|
||||||
|
unsigned i : 1;
|
||||||
|
unsigned unused : 20;
|
||||||
|
unsigned v : 1;
|
||||||
|
unsigned c : 1;
|
||||||
|
unsigned z : 1;
|
||||||
|
unsigned n : 1;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
#if defined(__BIG_ENDIAN__)
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t extension;
|
||||||
|
uint8_t control;
|
||||||
|
#else
|
||||||
|
uint8_t control;
|
||||||
|
uint8_t extension;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t flags;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
int32_t packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ARMMemory {
|
||||||
|
uint32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
||||||
|
uint32_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
||||||
|
uint32_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
||||||
|
|
||||||
|
void (*store32)(struct ARMCore*, uint32_t address, int32_t value, int* cycleCounter);
|
||||||
|
void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter);
|
||||||
|
void (*store8)(struct ARMCore*, uint32_t address, int8_t value, int* cycleCounter);
|
||||||
|
|
||||||
|
uint32_t (*loadMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
||||||
|
int* cycleCounter);
|
||||||
|
uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
||||||
|
int* cycleCounter);
|
||||||
|
|
||||||
|
uint32_t activeSeqCycles32;
|
||||||
|
// uint32_t activeSeqCycles16;
|
||||||
|
uint32_t activeNonseqCycles32;
|
||||||
|
// uint32_t activeNonseqCycles16;
|
||||||
|
int32_t (*stall)(struct ARMCore*, int32_t wait);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ARMInterruptHandler {
|
||||||
|
void (*reset)(struct ARMCore* cpu);
|
||||||
|
void (*processEvents)(struct ARMCore* cpu);
|
||||||
|
// void (*swi16)(struct ARMCore* cpu, int immediate);
|
||||||
|
void (*swi32)(struct ARMCore* cpu, int immediate);
|
||||||
|
void (*hitIllegal)(struct ARMCore* cpu, uint32_t opcode);
|
||||||
|
// void (*bkpt16)(struct ARMCore* cpu, int immediate);
|
||||||
|
void (*bkpt32)(struct ARMCore* cpu, int immediate);
|
||||||
|
void (*readCPSR)(struct ARMCore* cpu);
|
||||||
|
|
||||||
|
void (*hitStub)(struct ARMCore* cpu, uint32_t opcode);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ARMCore {
|
||||||
|
int32_t gprs[16];
|
||||||
|
union PSR cpsr;
|
||||||
|
union PSR spsr;
|
||||||
|
|
||||||
|
int64_t cycles;
|
||||||
|
int64_t nextEvent;
|
||||||
|
int halted;
|
||||||
|
|
||||||
|
int32_t bankedRegisters[6][7];
|
||||||
|
int32_t bankedSPSRs[6];
|
||||||
|
|
||||||
|
int32_t shifterOperand;
|
||||||
|
int32_t shifterCarryOut;
|
||||||
|
|
||||||
|
uint32_t prefetch[2];
|
||||||
|
enum PrivilegeMode privilegeMode;
|
||||||
|
|
||||||
|
struct ARMMemory memory;
|
||||||
|
struct ARMInterruptHandler irqh;
|
||||||
|
|
||||||
|
void *owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ARMReset(struct ARMCore* cpu);
|
||||||
|
void ARMSetPrivilegeMode(struct ARMCore*, enum PrivilegeMode);
|
||||||
|
void ARMRaiseIRQ(struct ARMCore*);
|
||||||
|
void ARMRaiseFIQ(struct ARMCore*);
|
||||||
|
void ARMRaiseSWI(struct ARMCore*);
|
||||||
|
void ARMRaiseUndefined(struct ARMCore*);
|
||||||
|
|
||||||
|
void ARMRun(struct ARMCore* cpu);
|
||||||
|
void ARMRunLoop(struct ARMCore* cpu);
|
||||||
|
void ARMRunFake(struct ARMCore* cpu, uint32_t opcode);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,280 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define CXX_GUARD_START extern "C" {
|
||||||
|
#define CXX_GUARD_END }
|
||||||
|
#else
|
||||||
|
#define CXX_GUARD_START
|
||||||
|
#define CXX_GUARD_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// WinSock2 gets very angry if it's included too late
|
||||||
|
#include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__cplusplus)
|
||||||
|
#define restrict __restrict
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
typedef intptr_t ssize_t;
|
||||||
|
#define PATH_MAX MAX_PATH
|
||||||
|
#define strcasecmp _stricmp
|
||||||
|
#define strncasecmp _strnicmp
|
||||||
|
#define ftruncate _chsize
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#define strdup _strdup
|
||||||
|
#define lseek _lseek
|
||||||
|
#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
|
||||||
|
#elif defined(__wii__)
|
||||||
|
#include <sys/time.h>
|
||||||
|
typedef intptr_t ssize_t;
|
||||||
|
#else
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PSP2
|
||||||
|
// For PATH_MAX on modern toolchains
|
||||||
|
#include <sys/syslimits.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSIZE_MAX
|
||||||
|
#define SSIZE_MAX ((ssize_t) (SIZE_MAX >> 1))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UNUSED
|
||||||
|
#define UNUSED(V) (void)(V)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.141592654f
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER) && (defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
|
||||||
|
#define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
|
||||||
|
#define ATOMIC_ADD(DST, OP) __atomic_add_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_SUB(DST, OP) __atomic_sub_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_OR(DST, OP) __atomic_or_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_AND(DST, OP) __atomic_and_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) __atomic_compare_exchange_n(&DST, &EXPECTED, SRC, true,__ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)
|
||||||
|
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||||
|
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||||
|
#elif defined _MSC_VER
|
||||||
|
#define ATOMIC_STORE(DST, SRC) InterlockedExchange(&DST, SRC)
|
||||||
|
#define ATOMIC_LOAD(DST, SRC) DST = InterlockedOrAcquire(&SRC, 0)
|
||||||
|
#define ATOMIC_ADD(DST, OP) InterlockedAddRelease(&DST, OP)
|
||||||
|
#define ATOMIC_SUB(DST, OP) InterlockedAddRelease(&DST, -OP)
|
||||||
|
#define ATOMIC_OR(DST, OP) InterlockedOrRelease(&DST, OP)
|
||||||
|
#define ATOMIC_AND(DST, OP) InterlockedAndRelease(&DST, OP)
|
||||||
|
#define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) (InterlockedCompareExchange(&DST, SRC, EXPECTED) == EXPECTED)
|
||||||
|
#define ATOMIC_STORE_PTR(DST, SRC) InterlockedExchangePointer(&DST, SRC)
|
||||||
|
#define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(&SRC, 0, 0)
|
||||||
|
#else
|
||||||
|
// TODO
|
||||||
|
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||||
|
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||||
|
#define ATOMIC_ADD(DST, OP) DST += OP
|
||||||
|
#define ATOMIC_SUB(DST, OP) DST -= OP
|
||||||
|
#define ATOMIC_OR(DST, OP) DST |= OP
|
||||||
|
#define ATOMIC_AND(DST, OP) DST &= OP
|
||||||
|
#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false)
|
||||||
|
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||||
|
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_3DS) || defined(GEKKO) || defined(PSP2)
|
||||||
|
// newlib doesn't support %z properly by default
|
||||||
|
#define PRIz ""
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
#define PRIz "ll"
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#define PRIz ""
|
||||||
|
#else
|
||||||
|
#define PRIz "z"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined __BIG_ENDIAN__
|
||||||
|
#define LOAD_32BE(DEST, ADDR, ARR) DEST = *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR))
|
||||||
|
#if defined(__PPC__) || defined(__POWERPC__)
|
||||||
|
#define LOAD_32LE(DEST, ADDR, ARR) { \
|
||||||
|
uint32_t _addr = (ADDR); \
|
||||||
|
const void* _ptr = (ARR); \
|
||||||
|
__asm__("lwbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOAD_16LE(DEST, ADDR, ARR) { \
|
||||||
|
uint32_t _addr = (ADDR); \
|
||||||
|
const void* _ptr = (ARR); \
|
||||||
|
__asm__("lhbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STORE_32LE(SRC, ADDR, ARR) { \
|
||||||
|
uint32_t _addr = (ADDR); \
|
||||||
|
void* _ptr = (ARR); \
|
||||||
|
__asm__("stwbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STORE_16LE(SRC, ADDR, ARR) { \
|
||||||
|
uint32_t _addr = (ADDR); \
|
||||||
|
void* _ptr = (ARR); \
|
||||||
|
__asm__("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOAD_64LE(DEST, ADDR, ARR) { \
|
||||||
|
uint32_t _addr = (ADDR); \
|
||||||
|
union { \
|
||||||
|
struct { \
|
||||||
|
uint32_t hi; \
|
||||||
|
uint32_t lo; \
|
||||||
|
}; \
|
||||||
|
uint64_t b64; \
|
||||||
|
} bswap; \
|
||||||
|
const void* _ptr = (ARR); \
|
||||||
|
__asm__( \
|
||||||
|
"lwbrx %0, %2, %3 \n" \
|
||||||
|
"lwbrx %1, %2, %4 \n" \
|
||||||
|
: "=&r"(bswap.lo), "=&r"(bswap.hi) : "b"(_ptr), "r"(_addr), "r"(_addr + 4)) ; \
|
||||||
|
DEST = bswap.b64; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STORE_64LE(SRC, ADDR, ARR) { \
|
||||||
|
uint32_t _addr = (ADDR); \
|
||||||
|
union { \
|
||||||
|
struct { \
|
||||||
|
uint32_t hi; \
|
||||||
|
uint32_t lo; \
|
||||||
|
}; \
|
||||||
|
uint64_t b64; \
|
||||||
|
} bswap = { .b64 = SRC }; \
|
||||||
|
const void* _ptr = (ARR); \
|
||||||
|
__asm__( \
|
||||||
|
"stwbrx %0, %2, %3 \n" \
|
||||||
|
"stwbrx %1, %2, %4 \n" \
|
||||||
|
: : "r"(bswap.hi), "r"(bswap.lo), "b"(_ptr), "r"(_addr), "r"(_addr + 4) : "memory"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
|
||||||
|
#define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3])
|
||||||
|
#define LOAD_32LE(DEST, ADDR, ARR) DEST = __builtin_bswap32(((uint32_t*) ARR)[(ADDR) >> 2])
|
||||||
|
#define LOAD_16LE(DEST, ADDR, ARR) DEST = __builtin_bswap16(((uint16_t*) ARR)[(ADDR) >> 1])
|
||||||
|
#define STORE_64LE(SRC, ADDR, ARR) ((uint64_t*) ARR)[(ADDR) >> 3] = __builtin_bswap64(SRC)
|
||||||
|
#define STORE_32LE(SRC, ADDR, ARR) ((uint32_t*) ARR)[(ADDR) >> 2] = __builtin_bswap32(SRC)
|
||||||
|
#define STORE_16LE(SRC, ADDR, ARR) ((uint16_t*) ARR)[(ADDR) >> 1] = __builtin_bswap16(SRC)
|
||||||
|
#else
|
||||||
|
#error Big endian build not supported on this platform.
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define LOAD_64LE(DEST, ADDR, ARR) DEST = *(uint64_t*) ((uintptr_t) (ARR) + (size_t) (ADDR))
|
||||||
|
#define LOAD_32LE(DEST, ADDR, ARR) DEST = *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR))
|
||||||
|
#define LOAD_16LE(DEST, ADDR, ARR) DEST = *(uint16_t*) ((uintptr_t) (ARR) + (size_t) (ADDR))
|
||||||
|
#define STORE_64LE(SRC, ADDR, ARR) *(uint64_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) = SRC
|
||||||
|
#define STORE_32LE(SRC, ADDR, ARR) *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) = SRC
|
||||||
|
#define STORE_16LE(SRC, ADDR, ARR) *(uint16_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) = SRC
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define LOAD_32BE(DEST, ADDR, ARR) DEST = _byteswap_ulong(((uint32_t*) ARR)[(ADDR) >> 2])
|
||||||
|
#else
|
||||||
|
#define LOAD_32BE(DEST, ADDR, ARR) DEST = __builtin_bswap32(((uint32_t*) ARR)[(ADDR) >> 2])
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAKE_MASK(START, END) (((1 << ((END) - (START))) - 1) << (START))
|
||||||
|
#define CHECK_BITS(SRC, START, END) ((SRC) & MAKE_MASK(START, END))
|
||||||
|
#define EXT_BITS(SRC, START, END) (((SRC) >> (START)) & ((1 << ((END) - (START))) - 1))
|
||||||
|
#define INS_BITS(SRC, START, END, BITS) (CLEAR_BITS(SRC, START, END) | (((BITS) << (START)) & MAKE_MASK(START, END)))
|
||||||
|
#define CLEAR_BITS(SRC, START, END) ((SRC) & ~MAKE_MASK(START, END))
|
||||||
|
#define FILL_BITS(SRC, START, END) ((SRC) | MAKE_MASK(START, END))
|
||||||
|
#define TEST_FILL_BITS(SRC, START, END, TEST) ((TEST) ? (FILL_BITS(SRC, START, END)) : (CLEAR_BITS(SRC, START, END)))
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma section(".CRT$XCU",read)
|
||||||
|
#define ATTRIBUTE_UNUSED
|
||||||
|
#define ATTRIBUTE_FORMAT(X, Y, Z)
|
||||||
|
#define ATTRIBUTE_NOINLINE
|
||||||
|
// Adapted from https://stackoverflow.com/a/2390626
|
||||||
|
#define _CONSTRUCTOR(FN, PRE) \
|
||||||
|
static void FN(void); \
|
||||||
|
__declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \
|
||||||
|
static void FN(void)
|
||||||
|
#ifdef _WIN64
|
||||||
|
#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "")
|
||||||
|
#else
|
||||||
|
#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "_")
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||||
|
#define ATTRIBUTE_FORMAT(X, Y, Z) __attribute__((format(X, Y, Z)))
|
||||||
|
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||||
|
#define CONSTRUCTOR(FN) static __attribute__((constructor)) void FN(void)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME
|
||||||
|
|
||||||
|
#define DECL_BITS(TYPE, FIELD, START, SIZE) \
|
||||||
|
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Is ## FIELD (TYPE src) { \
|
||||||
|
return CHECK_BITS(src, (START), (START) + (SIZE)); \
|
||||||
|
} \
|
||||||
|
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Get ## FIELD (TYPE src) { \
|
||||||
|
return EXT_BITS(src, (START), (START) + (SIZE)); \
|
||||||
|
} \
|
||||||
|
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Clear ## FIELD (TYPE src) { \
|
||||||
|
return CLEAR_BITS(src, (START), (START) + (SIZE)); \
|
||||||
|
} \
|
||||||
|
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Fill ## FIELD (TYPE src) { \
|
||||||
|
return FILL_BITS(src, (START), (START) + (SIZE)); \
|
||||||
|
} \
|
||||||
|
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Set ## FIELD (TYPE src, TYPE bits) { \
|
||||||
|
return INS_BITS(src, (START), (START) + (SIZE), bits); \
|
||||||
|
} \
|
||||||
|
ATTRIBUTE_UNUSED static inline TYPE TYPE ## TestFill ## FIELD (TYPE src, bool test) { \
|
||||||
|
return TEST_FILL_BITS(src, (START), (START) + (SIZE), test); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_BIT(TYPE, FIELD, BIT) DECL_BITS(TYPE, FIELD, BIT, 1)
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#define LIKELY(X) __builtin_expect(!!(X), 1)
|
||||||
|
#define UNLIKELY(X) __builtin_expect(!!(X), 0)
|
||||||
|
#else
|
||||||
|
#define LIKELY(X) (!!(X))
|
||||||
|
#define UNLIKELY(X) (!!(X))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ROR(I, ROTATE) ((((uint32_t) (I)) >> ROTATE) | ((uint32_t) (I) << ((-ROTATE) & 31)))
|
||||||
|
|
||||||
|
static inline uint32_t popcount32(unsigned bits) {
|
||||||
|
bits = bits - ((bits >> 1) & 0x55555555);
|
||||||
|
bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
|
||||||
|
return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,447 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "decoder.h"
|
||||||
|
|
||||||
|
#include "decoder-inlines.h"
|
||||||
|
#include "emitter-arm.h"
|
||||||
|
#include "isa-inlines.h"
|
||||||
|
|
||||||
|
#define ADDR_MODE_1_SHIFT(OP) \
|
||||||
|
info->op3.reg = opcode & 0x0000000F; \
|
||||||
|
info->op3.shifterOp = ARM_SHIFT_ ## OP; \
|
||||||
|
info->operandFormat |= ARM_OPERAND_REGISTER_3; \
|
||||||
|
if (opcode & 0x00000010) { \
|
||||||
|
info->op3.shifterReg = (opcode >> 8) & 0xF; \
|
||||||
|
++info->iCycles; \
|
||||||
|
info->operandFormat |= ARM_OPERAND_SHIFT_REGISTER_3; \
|
||||||
|
} else { \
|
||||||
|
info->op3.shifterImm = (opcode >> 7) & 0x1F; \
|
||||||
|
info->operandFormat |= ARM_OPERAND_SHIFT_IMMEDIATE_3; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_1_LSL \
|
||||||
|
ADDR_MODE_1_SHIFT(LSL) \
|
||||||
|
if (!info->op3.shifterImm) { \
|
||||||
|
info->operandFormat &= ~ARM_OPERAND_SHIFT_IMMEDIATE_3; \
|
||||||
|
info->op3.shifterOp = ARM_SHIFT_NONE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_1_LSR ADDR_MODE_1_SHIFT(LSR)
|
||||||
|
#define ADDR_MODE_1_ASR ADDR_MODE_1_SHIFT(ASR)
|
||||||
|
#define ADDR_MODE_1_ROR \
|
||||||
|
ADDR_MODE_1_SHIFT(ROR) \
|
||||||
|
if (!info->op3.shifterImm) { \
|
||||||
|
info->op3.shifterOp = ARM_SHIFT_RRX; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_1_IMM \
|
||||||
|
int rotate = (opcode & 0x00000F00) >> 7; \
|
||||||
|
int immediate = opcode & 0x000000FF; \
|
||||||
|
info->op3.immediate = ROR(immediate, rotate); \
|
||||||
|
info->operandFormat |= ARM_OPERAND_IMMEDIATE_3;
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_SHIFT(OP) \
|
||||||
|
info->memory.format |= ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_SHIFTED_OFFSET; \
|
||||||
|
info->memory.offset.shifterOp = ARM_SHIFT_ ## OP; \
|
||||||
|
info->memory.offset.shifterImm = (opcode >> 7) & 0x1F; \
|
||||||
|
info->memory.offset.reg = opcode & 0x0000000F;
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_LSL \
|
||||||
|
ADDR_MODE_2_SHIFT(LSL) \
|
||||||
|
if (!info->memory.offset.shifterImm) { \
|
||||||
|
info->memory.format &= ~ARM_MEMORY_SHIFTED_OFFSET; \
|
||||||
|
info->memory.offset.shifterOp = ARM_SHIFT_NONE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_LSR ADDR_MODE_2_SHIFT(LSR) \
|
||||||
|
if (!info->memory.offset.shifterImm) { \
|
||||||
|
info->memory.offset.shifterImm = 32; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_ASR ADDR_MODE_2_SHIFT(ASR) \
|
||||||
|
if (!info->memory.offset.shifterImm) { \
|
||||||
|
info->memory.offset.shifterImm = 32; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_ROR \
|
||||||
|
ADDR_MODE_2_SHIFT(ROR) \
|
||||||
|
if (!info->memory.offset.shifterImm) { \
|
||||||
|
info->memory.offset.shifterOp = ARM_SHIFT_RRX; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_IMM \
|
||||||
|
info->memory.format |= ARM_MEMORY_IMMEDIATE_OFFSET; \
|
||||||
|
info->memory.offset.immediate = opcode & 0x00000FFF;
|
||||||
|
|
||||||
|
#define ADDR_MODE_3_REG \
|
||||||
|
info->memory.format |= ARM_MEMORY_REGISTER_OFFSET; \
|
||||||
|
info->memory.offset.reg = opcode & 0x0000000F;
|
||||||
|
|
||||||
|
#define ADDR_MODE_3_IMM \
|
||||||
|
info->memory.format |= ARM_MEMORY_IMMEDIATE_OFFSET; \
|
||||||
|
info->memory.offset.immediate = (opcode & 0x0000000F) | ((opcode & 0x00000F00) >> 4);
|
||||||
|
|
||||||
|
#define DEFINE_DECODER_ARM(NAME, MNEMONIC, BODY) \
|
||||||
|
static void _ARMDecode ## NAME (uint32_t opcode, struct ARMInstructionInfo* info) { \
|
||||||
|
UNUSED(opcode); \
|
||||||
|
info->mnemonic = ARM_MN_ ## MNEMONIC; \
|
||||||
|
BODY; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_ALU_DECODER_EX_ARM(NAME, MNEMONIC, S, SHIFTER, OTHER_AFFECTED, SKIPPED) \
|
||||||
|
DEFINE_DECODER_ARM(NAME, MNEMONIC, \
|
||||||
|
info->op1.reg = (opcode >> 12) & 0xF; \
|
||||||
|
info->op2.reg = (opcode >> 16) & 0xF; \
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 | \
|
||||||
|
OTHER_AFFECTED | \
|
||||||
|
ARM_OPERAND_REGISTER_2; \
|
||||||
|
info->affectsCPSR = S; \
|
||||||
|
SHIFTER; \
|
||||||
|
if (SKIPPED == 1) { \
|
||||||
|
info->op1 = info->op2; \
|
||||||
|
info->op2 = info->op3; \
|
||||||
|
info->operandFormat >>= 8; \
|
||||||
|
} else if (SKIPPED == 2) { \
|
||||||
|
info->op2 = info->op3; \
|
||||||
|
info->operandFormat |= info->operandFormat >> 8; \
|
||||||
|
info->operandFormat &= ~ARM_OPERAND_3; \
|
||||||
|
} \
|
||||||
|
if (info->op1.reg == ARM_PC) { \
|
||||||
|
info->branchType = ARM_BRANCH_INDIRECT; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define DEFINE_ALU_DECODER_ARM(NAME, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSL, NAME, 0, ADDR_MODE_1_LSL, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSL, NAME, 1, ADDR_MODE_1_LSL, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSR, NAME, 0, ADDR_MODE_1_LSR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSR, NAME, 1, ADDR_MODE_1_LSR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASR, NAME, 0, ADDR_MODE_1_ASR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ASR, NAME, 1, ADDR_MODE_1_ASR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _ROR, NAME, 0, ADDR_MODE_1_ROR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ROR, NAME, 1, ADDR_MODE_1_ROR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## I, NAME, 0, ADDR_MODE_1_IMM, ARM_OPERAND_AFFECTED_1, SKIPPED) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## SI, NAME, 1, ADDR_MODE_1_IMM, ARM_OPERAND_AFFECTED_1, SKIPPED)
|
||||||
|
|
||||||
|
#define DEFINE_ALU_DECODER_S_ONLY_ARM(NAME) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSL, NAME, 1, ADDR_MODE_1_LSL, ARM_OPERAND_NONE, 1) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSR, NAME, 1, ADDR_MODE_1_LSR, ARM_OPERAND_NONE, 1) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASR, NAME, 1, ADDR_MODE_1_ASR, ARM_OPERAND_NONE, 1) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## _ROR, NAME, 1, ADDR_MODE_1_ROR, ARM_OPERAND_NONE, 1) \
|
||||||
|
DEFINE_ALU_DECODER_EX_ARM(NAME ## I, NAME, 1, ADDR_MODE_1_IMM, ARM_OPERAND_NONE, 1)
|
||||||
|
|
||||||
|
#define DEFINE_MULTIPLY_DECODER_EX_ARM(NAME, MNEMONIC, S, OTHER_AFFECTED) \
|
||||||
|
DEFINE_DECODER_ARM(NAME, MNEMONIC, \
|
||||||
|
info->op1.reg = (opcode >> 16) & 0xF; \
|
||||||
|
info->op2.reg = opcode & 0xF; \
|
||||||
|
info->op3.reg = (opcode >> 8) & 0xF; \
|
||||||
|
info->op4.reg = (opcode >> 12) & 0xF; \
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 | \
|
||||||
|
ARM_OPERAND_AFFECTED_1 | \
|
||||||
|
ARM_OPERAND_REGISTER_2 | \
|
||||||
|
ARM_OPERAND_REGISTER_3 | \
|
||||||
|
OTHER_AFFECTED; \
|
||||||
|
info->affectsCPSR = S; \
|
||||||
|
if (info->op1.reg == ARM_PC) { \
|
||||||
|
info->branchType = ARM_BRANCH_INDIRECT; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME, MNEMONIC, S) \
|
||||||
|
DEFINE_DECODER_ARM(NAME, MNEMONIC, \
|
||||||
|
info->op1.reg = (opcode >> 12) & 0xF; \
|
||||||
|
info->op2.reg = (opcode >> 16) & 0xF; \
|
||||||
|
info->op3.reg = opcode & 0xF; \
|
||||||
|
info->op4.reg = (opcode >> 8) & 0xF; \
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 | \
|
||||||
|
ARM_OPERAND_AFFECTED_1 | \
|
||||||
|
ARM_OPERAND_REGISTER_2 | \
|
||||||
|
ARM_OPERAND_AFFECTED_2 | \
|
||||||
|
ARM_OPERAND_REGISTER_3 | \
|
||||||
|
ARM_OPERAND_REGISTER_4; \
|
||||||
|
info->affectsCPSR = S; \
|
||||||
|
if (info->op1.reg == ARM_PC) { \
|
||||||
|
info->branchType = ARM_BRANCH_INDIRECT; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define DEFINE_MULTIPLY_DECODER_ARM(NAME, OTHER_AFFECTED) \
|
||||||
|
DEFINE_MULTIPLY_DECODER_EX_ARM(NAME, NAME, 0, OTHER_AFFECTED) \
|
||||||
|
DEFINE_MULTIPLY_DECODER_EX_ARM(NAME ## S, NAME, 1, OTHER_AFFECTED)
|
||||||
|
|
||||||
|
#define DEFINE_LONG_MULTIPLY_DECODER_ARM(NAME) \
|
||||||
|
DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME, NAME, 0) \
|
||||||
|
DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME ## S, NAME, 1)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, ADDRESSING_MODE, ADDRESSING_DECODING, CYCLES, TYPE) \
|
||||||
|
DEFINE_DECODER_ARM(NAME, MNEMONIC, \
|
||||||
|
info->op1.reg = (opcode >> 12) & 0xF; \
|
||||||
|
info->memory.baseReg = (opcode >> 16) & 0xF; \
|
||||||
|
info->memory.width = TYPE; \
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 | \
|
||||||
|
ARM_OPERAND_AFFECTED_1 | /* TODO: Remove this for STR */ \
|
||||||
|
ARM_OPERAND_MEMORY_2; \
|
||||||
|
info->memory.format = ARM_MEMORY_REGISTER_BASE | ADDRESSING_MODE; \
|
||||||
|
ADDRESSING_DECODING; \
|
||||||
|
CYCLES;)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \
|
||||||
|
ARM_MEMORY_POST_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK | \
|
||||||
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \
|
||||||
|
ARM_MEMORY_POST_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## P, MNEMONIC, \
|
||||||
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PW, MNEMONIC, \
|
||||||
|
ARM_MEMORY_PRE_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK | \
|
||||||
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PU, MNEMONIC, \
|
||||||
|
0, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PUW, MNEMONIC, \
|
||||||
|
ARM_MEMORY_WRITEBACK, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _LSL_, MNEMONIC, ADDR_MODE_2_LSL, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _LSR_, MNEMONIC, ADDR_MODE_2_LSR, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _ASR_, MNEMONIC, ADDR_MODE_2_ASR, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _ROR_, MNEMONIC, ADDR_MODE_2_ROR, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_2_IMM, CYCLES, TYPE)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME, MNEMONIC, ADDR_MODE_3_REG, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_3_IMM, CYCLES, TYPE)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \
|
||||||
|
ARM_MEMORY_POST_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK | \
|
||||||
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \
|
||||||
|
ARM_MEMORY_POST_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK, \
|
||||||
|
ADDRESSING_MODE, CYCLES, TYPE)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_T_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _LSL_, MNEMONIC, ADDR_MODE_2_LSL, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _LSR_, MNEMONIC, ADDR_MODE_2_LSR, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _ASR_, MNEMONIC, ADDR_MODE_2_ASR, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _ROR_, MNEMONIC, ADDR_MODE_2_ROR, CYCLES, TYPE) \
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_2_IMM, CYCLES, TYPE)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME, MNEMONIC, DIRECTION, WRITEBACK) \
|
||||||
|
DEFINE_DECODER_ARM(NAME, MNEMONIC, \
|
||||||
|
info->memory.baseReg = (opcode >> 16) & 0xF; \
|
||||||
|
info->op1.immediate = opcode & 0x0000FFFF; \
|
||||||
|
if (info->op1.immediate & (1 << ARM_PC)) { \
|
||||||
|
info->branchType = ARM_BRANCH_INDIRECT; \
|
||||||
|
} \
|
||||||
|
info->operandFormat = ARM_OPERAND_MEMORY_1; \
|
||||||
|
info->memory.format = ARM_MEMORY_REGISTER_BASE | \
|
||||||
|
WRITEBACK | \
|
||||||
|
ARM_MEMORY_ ## DIRECTION;)
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(NAME) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DA, NAME, DECREMENT_AFTER, 0) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DAW, NAME, DECREMENT_AFTER, ARM_MEMORY_WRITEBACK) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DB, NAME, DECREMENT_BEFORE, 0) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DBW, NAME, DECREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IA, NAME, INCREMENT_AFTER, 0) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IAW, NAME, INCREMENT_AFTER, ARM_MEMORY_WRITEBACK) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IB, NAME, INCREMENT_BEFORE, 0) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IBW, NAME, INCREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDA, NAME, DECREMENT_AFTER, ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDAW, NAME, DECREMENT_AFTER, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDB, NAME, DECREMENT_BEFORE, ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDBW, NAME, DECREMENT_BEFORE, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIA, NAME, INCREMENT_AFTER, ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIAW, NAME, INCREMENT_AFTER, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIB, NAME, INCREMENT_BEFORE, ARM_MEMORY_SPSR_SWAP) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIBW, NAME, INCREMENT_BEFORE, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP)
|
||||||
|
|
||||||
|
#define DEFINE_SWP_DECODER_ARM(NAME, TYPE) \
|
||||||
|
DEFINE_DECODER_ARM(NAME, SWP, \
|
||||||
|
info->memory.baseReg = (opcode >> 16) & 0xF; \
|
||||||
|
info->op1.reg = (opcode >> 12) & 0xF; \
|
||||||
|
info->op2.reg = opcode & 0xF; \
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 | \
|
||||||
|
ARM_OPERAND_AFFECTED_1 | \
|
||||||
|
ARM_OPERAND_REGISTER_2 | \
|
||||||
|
ARM_OPERAND_MEMORY_3; \
|
||||||
|
info->memory.format = ARM_MEMORY_REGISTER_BASE; \
|
||||||
|
info->memory.width = TYPE;)
|
||||||
|
|
||||||
|
DEFINE_ALU_DECODER_ARM(ADD, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(ADC, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(AND, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(BIC, 0)
|
||||||
|
DEFINE_ALU_DECODER_S_ONLY_ARM(CMN)
|
||||||
|
DEFINE_ALU_DECODER_S_ONLY_ARM(CMP)
|
||||||
|
DEFINE_ALU_DECODER_ARM(EOR, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(MOV, 2)
|
||||||
|
DEFINE_ALU_DECODER_ARM(MVN, 2)
|
||||||
|
DEFINE_ALU_DECODER_ARM(ORR, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(RSB, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(RSC, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(SBC, 0)
|
||||||
|
DEFINE_ALU_DECODER_ARM(SUB, 0)
|
||||||
|
DEFINE_ALU_DECODER_S_ONLY_ARM(TEQ)
|
||||||
|
DEFINE_ALU_DECODER_S_ONLY_ARM(TST)
|
||||||
|
|
||||||
|
// TOOD: Estimate cycles
|
||||||
|
DEFINE_MULTIPLY_DECODER_ARM(MLA, ARM_OPERAND_REGISTER_4)
|
||||||
|
DEFINE_MULTIPLY_DECODER_ARM(MUL, ARM_OPERAND_NONE)
|
||||||
|
|
||||||
|
DEFINE_LONG_MULTIPLY_DECODER_ARM(SMLAL)
|
||||||
|
DEFINE_LONG_MULTIPLY_DECODER_ARM(SMULL)
|
||||||
|
DEFINE_LONG_MULTIPLY_DECODER_ARM(UMLAL)
|
||||||
|
DEFINE_LONG_MULTIPLY_DECODER_ARM(UMULL)
|
||||||
|
|
||||||
|
// Begin load/store definitions
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(LDR, LDR, LOAD_CYCLES, ARM_ACCESS_WORD)
|
||||||
|
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(LDRB, LDR, LOAD_CYCLES, ARM_ACCESS_BYTE)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRH, LDR, LOAD_CYCLES, ARM_ACCESS_HALFWORD)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRSB, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_BYTE)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRSH, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_HALFWORD)
|
||||||
|
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(STR, STR, STORE_CYCLES, ARM_ACCESS_WORD)
|
||||||
|
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(STRB, STR, STORE_CYCLES, ARM_ACCESS_BYTE)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(STRH, STR, STORE_CYCLES, ARM_ACCESS_HALFWORD)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_ARM(LDRBT, LDR, LOAD_CYCLES, ARM_ACCESS_TRANSLATED_BYTE)
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_ARM(LDRT, LDR, LOAD_CYCLES, ARM_ACCESS_TRANSLATED_WORD)
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_ARM(STRBT, STR, STORE_CYCLES, ARM_ACCESS_TRANSLATED_BYTE)
|
||||||
|
DEFINE_LOAD_STORE_T_DECODER_ARM(STRT, STR, STORE_CYCLES, ARM_ACCESS_TRANSLATED_WORD)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(LDM)
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(STM)
|
||||||
|
|
||||||
|
DEFINE_SWP_DECODER_ARM(SWP, ARM_ACCESS_WORD)
|
||||||
|
DEFINE_SWP_DECODER_ARM(SWPB, ARM_ACCESS_BYTE)
|
||||||
|
|
||||||
|
// End load/store definitions
|
||||||
|
|
||||||
|
// Begin branch definitions
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(B, B,
|
||||||
|
int32_t offset = opcode << 8;
|
||||||
|
info->op1.immediate = offset >> 6;
|
||||||
|
info->operandFormat = ARM_OPERAND_IMMEDIATE_1;
|
||||||
|
info->branchType = ARM_BRANCH;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(BL, BL,
|
||||||
|
int32_t offset = opcode << 8;
|
||||||
|
info->op1.immediate = offset >> 6;
|
||||||
|
info->operandFormat = ARM_OPERAND_IMMEDIATE_1;
|
||||||
|
info->branchType = ARM_BRANCH_LINKED;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(BX, BX,
|
||||||
|
info->op1.reg = opcode & 0x0000000F;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1;
|
||||||
|
info->branchType = ARM_BRANCH_INDIRECT;)
|
||||||
|
|
||||||
|
// End branch definitions
|
||||||
|
|
||||||
|
// Begin coprocessor definitions
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(CDP, ILL, info->operandFormat = ARM_OPERAND_NONE;)
|
||||||
|
DEFINE_DECODER_ARM(LDC, ILL, info->operandFormat = ARM_OPERAND_NONE;)
|
||||||
|
DEFINE_DECODER_ARM(STC, ILL, info->operandFormat = ARM_OPERAND_NONE;)
|
||||||
|
DEFINE_DECODER_ARM(MCR, ILL, info->operandFormat = ARM_OPERAND_NONE;)
|
||||||
|
DEFINE_DECODER_ARM(MRC, ILL, info->operandFormat = ARM_OPERAND_NONE;)
|
||||||
|
|
||||||
|
// Begin miscellaneous definitions
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(BKPT, BKPT,
|
||||||
|
info->operandFormat = ARM_OPERAND_NONE;
|
||||||
|
info->traps = 1;) // Not strictly in ARMv4T, but here for convenience
|
||||||
|
DEFINE_DECODER_ARM(ILL, ILL,
|
||||||
|
info->operandFormat = ARM_OPERAND_NONE;
|
||||||
|
info->traps = 1;) // Illegal opcode
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(MSR, MSR,
|
||||||
|
info->affectsCPSR = 1;
|
||||||
|
info->op1.reg = ARM_CPSR;
|
||||||
|
info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
|
||||||
|
info->op2.reg = opcode & 0x0000000F;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 |
|
||||||
|
ARM_OPERAND_AFFECTED_1 |
|
||||||
|
ARM_OPERAND_REGISTER_2;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(MSRR, MSR,
|
||||||
|
info->op1.reg = ARM_SPSR;
|
||||||
|
info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
|
||||||
|
info->op2.reg = opcode & 0x0000000F;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 |
|
||||||
|
ARM_OPERAND_AFFECTED_1 |
|
||||||
|
ARM_OPERAND_REGISTER_2;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(MRS, MRS,
|
||||||
|
info->affectsCPSR = 1;
|
||||||
|
info->op1.reg = (opcode >> 12) & 0xF;
|
||||||
|
info->op2.reg = ARM_CPSR;
|
||||||
|
info->op2.psrBits = 0;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 |
|
||||||
|
ARM_OPERAND_AFFECTED_1 |
|
||||||
|
ARM_OPERAND_REGISTER_2;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(MRSR, MRS,
|
||||||
|
info->op1.reg = (opcode >> 12) & 0xF;
|
||||||
|
info->op2.reg = ARM_SPSR;
|
||||||
|
info->op2.psrBits = 0;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 |
|
||||||
|
ARM_OPERAND_AFFECTED_1 |
|
||||||
|
ARM_OPERAND_REGISTER_2;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(MSRI, MSR,
|
||||||
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
|
info->affectsCPSR = 1;
|
||||||
|
info->op1.reg = ARM_CPSR;
|
||||||
|
info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
|
||||||
|
info->op2.immediate = operand;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 |
|
||||||
|
ARM_OPERAND_AFFECTED_1 |
|
||||||
|
ARM_OPERAND_IMMEDIATE_2;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(MSRRI, MSR,
|
||||||
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
|
info->op1.reg = ARM_SPSR;
|
||||||
|
info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
|
||||||
|
info->op2.immediate = operand;
|
||||||
|
info->operandFormat = ARM_OPERAND_REGISTER_1 |
|
||||||
|
ARM_OPERAND_AFFECTED_1 |
|
||||||
|
ARM_OPERAND_IMMEDIATE_2;)
|
||||||
|
|
||||||
|
DEFINE_DECODER_ARM(SWI, SWI,
|
||||||
|
info->op1.immediate = opcode & 0xFFFFFF;
|
||||||
|
info->operandFormat = ARM_OPERAND_IMMEDIATE_1;
|
||||||
|
info->traps = 1;)
|
||||||
|
|
||||||
|
typedef void (*ARMDecoder)(uint32_t opcode, struct ARMInstructionInfo* info);
|
||||||
|
|
||||||
|
static const ARMDecoder _armDecoderTable[0x1000] = {
|
||||||
|
DECLARE_ARM_EMITTER_BLOCK(_ARMDecode)
|
||||||
|
};
|
||||||
|
|
||||||
|
void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info) {
|
||||||
|
memset(info, 0, sizeof(*info));
|
||||||
|
info->opcode = opcode;
|
||||||
|
info->branchType = ARM_BRANCH_NONE;
|
||||||
|
info->condition = opcode >> 28;
|
||||||
|
info->sInstructionCycles = 1;
|
||||||
|
ARMDecoder decoder = _armDecoderTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
|
||||||
|
decoder(opcode, info);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef ARM_DECODER_INLINES_H
|
||||||
|
#define ARM_DECODER_INLINES_H
|
||||||
|
|
||||||
|
#include "decoder.h"
|
||||||
|
|
||||||
|
#include "arm.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define LOAD_CYCLES \
|
||||||
|
info->iCycles = 1; \
|
||||||
|
info->nDataCycles = 1;
|
||||||
|
|
||||||
|
#define STORE_CYCLES \
|
||||||
|
info->sInstructionCycles = 0; \
|
||||||
|
info->nInstructionCycles = 1; \
|
||||||
|
info->nDataCycles = 1;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,479 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "decoder.h"
|
||||||
|
|
||||||
|
#include "decoder-inlines.h"
|
||||||
|
|
||||||
|
#define ADVANCE(AMOUNT) \
|
||||||
|
if (AMOUNT >= blen) { \
|
||||||
|
buffer[blen - 1] = '\0'; \
|
||||||
|
return total; \
|
||||||
|
} \
|
||||||
|
total += AMOUNT; \
|
||||||
|
buffer += AMOUNT; \
|
||||||
|
blen -= AMOUNT;
|
||||||
|
|
||||||
|
static int _decodeRegister(int reg, char* buffer, int blen);
|
||||||
|
static int _decodeRegisterList(int list, char* buffer, int blen);
|
||||||
|
static int _decodePSR(int bits, char* buffer, int blen);
|
||||||
|
static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen);
|
||||||
|
static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen);
|
||||||
|
static int _decodeShift(union ARMOperand operand, bool reg, char* buffer, int blen);
|
||||||
|
|
||||||
|
static const char* _armConditions[] = {
|
||||||
|
"eq",
|
||||||
|
"ne",
|
||||||
|
"cs",
|
||||||
|
"cc",
|
||||||
|
"mi",
|
||||||
|
"pl",
|
||||||
|
"vs",
|
||||||
|
"vc",
|
||||||
|
"hi",
|
||||||
|
"ls",
|
||||||
|
"ge",
|
||||||
|
"lt",
|
||||||
|
"gt",
|
||||||
|
"le",
|
||||||
|
"al",
|
||||||
|
"nv"
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _decodeRegister(int reg, char* buffer, int blen) {
|
||||||
|
switch (reg) {
|
||||||
|
case ARM_SP:
|
||||||
|
strncpy(buffer, "sp", blen - 1);
|
||||||
|
return 2;
|
||||||
|
case ARM_LR:
|
||||||
|
strncpy(buffer, "lr", blen - 1);
|
||||||
|
return 2;
|
||||||
|
case ARM_PC:
|
||||||
|
strncpy(buffer, "pc", blen - 1);
|
||||||
|
return 2;
|
||||||
|
case ARM_CPSR:
|
||||||
|
strncpy(buffer, "cpsr", blen - 1);
|
||||||
|
return 4;
|
||||||
|
case ARM_SPSR:
|
||||||
|
strncpy(buffer, "spsr", blen - 1);
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return snprintf(buffer, blen - 1, "r%i", reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _decodeRegisterList(int list, char* buffer, int blen) {
|
||||||
|
if (blen <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int total = 0;
|
||||||
|
strncpy(buffer, "{", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
int i;
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
|
int written;
|
||||||
|
for (i = 0; i <= ARM_PC; ++i) {
|
||||||
|
if (list & 1) {
|
||||||
|
if (start < 0) {
|
||||||
|
start = i;
|
||||||
|
end = i;
|
||||||
|
} else if (end + 1 == i) {
|
||||||
|
end = i;
|
||||||
|
} else {
|
||||||
|
if (end > start) {
|
||||||
|
written = _decodeRegister(start, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
strncpy(buffer, "-", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
written = _decodeRegister(end, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
strncpy(buffer, ",", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
start = i;
|
||||||
|
end = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list >>= 1;
|
||||||
|
}
|
||||||
|
if (start >= 0) {
|
||||||
|
if (end > start) {
|
||||||
|
written = _decodeRegister(start, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
strncpy(buffer, "-", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
written = _decodeRegister(end, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
strncpy(buffer, "}", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _decodePSR(int psrBits, char* buffer, int blen) {
|
||||||
|
if (!psrBits) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int total = 0;
|
||||||
|
strncpy(buffer, "_", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
if (psrBits & ARM_PSR_C) {
|
||||||
|
strncpy(buffer, "c", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
if (psrBits & ARM_PSR_X) {
|
||||||
|
strncpy(buffer, "x", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
if (psrBits & ARM_PSR_S) {
|
||||||
|
strncpy(buffer, "s", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
if (psrBits & ARM_PSR_F) {
|
||||||
|
strncpy(buffer, "f", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen) {
|
||||||
|
return snprintf(buffer, blen - 1, "$%08X", address + pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen) {
|
||||||
|
if (blen <= 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int total = 0;
|
||||||
|
strncpy(buffer, "[", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
int written;
|
||||||
|
if (memory.format & ARM_MEMORY_REGISTER_BASE) {
|
||||||
|
if (memory.baseReg == ARM_PC && memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) {
|
||||||
|
written = _decodePCRelative(memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -memory.offset.immediate : memory.offset.immediate, pc & 0xFFFFFFFC, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else {
|
||||||
|
written = _decodeRegister(memory.baseReg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) {
|
||||||
|
strncpy(buffer, ", ", blen - 1);
|
||||||
|
ADVANCE(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (memory.format & ARM_MEMORY_POST_INCREMENT) {
|
||||||
|
strncpy(buffer, "], ", blen - 1);
|
||||||
|
ADVANCE(3);
|
||||||
|
}
|
||||||
|
if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) {
|
||||||
|
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
|
||||||
|
written = snprintf(buffer, blen - 1, "#-%i", memory.offset.immediate);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else {
|
||||||
|
written = snprintf(buffer, blen - 1, "#%i", memory.offset.immediate);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
} else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) {
|
||||||
|
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
|
||||||
|
strncpy(buffer, "-", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
written = _decodeRegister(memory.offset.reg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (memory.format & ARM_MEMORY_SHIFTED_OFFSET) {
|
||||||
|
written = _decodeShift(memory.offset, false, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(memory.format & ARM_MEMORY_POST_INCREMENT)) {
|
||||||
|
strncpy(buffer, "]", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) {
|
||||||
|
strncpy(buffer, "!", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _decodeShift(union ARMOperand op, bool reg, char* buffer, int blen) {
|
||||||
|
if (blen <= 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int total = 0;
|
||||||
|
strncpy(buffer, ", ", blen - 1);
|
||||||
|
ADVANCE(2);
|
||||||
|
int written;
|
||||||
|
switch (op.shifterOp) {
|
||||||
|
case ARM_SHIFT_LSL:
|
||||||
|
strncpy(buffer, "lsl ", blen - 1);
|
||||||
|
ADVANCE(4);
|
||||||
|
break;
|
||||||
|
case ARM_SHIFT_LSR:
|
||||||
|
strncpy(buffer, "lsr ", blen - 1);
|
||||||
|
ADVANCE(4);
|
||||||
|
break;
|
||||||
|
case ARM_SHIFT_ASR:
|
||||||
|
strncpy(buffer, "asr ", blen - 1);
|
||||||
|
ADVANCE(4);
|
||||||
|
break;
|
||||||
|
case ARM_SHIFT_ROR:
|
||||||
|
strncpy(buffer, "ror ", blen - 1);
|
||||||
|
ADVANCE(4);
|
||||||
|
break;
|
||||||
|
case ARM_SHIFT_RRX:
|
||||||
|
strncpy(buffer, "rrx", blen - 1);
|
||||||
|
ADVANCE(3);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
if (!reg) {
|
||||||
|
written = snprintf(buffer, blen - 1, "#%i", op.shifterImm);
|
||||||
|
} else {
|
||||||
|
written = _decodeRegister(op.shifterReg, buffer, blen);
|
||||||
|
}
|
||||||
|
ADVANCE(written);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* _armMnemonicStrings[] = {
|
||||||
|
"ill",
|
||||||
|
"adc",
|
||||||
|
"add",
|
||||||
|
"and",
|
||||||
|
"asr",
|
||||||
|
"b",
|
||||||
|
"bic",
|
||||||
|
"bkpt",
|
||||||
|
"bl",
|
||||||
|
"bx",
|
||||||
|
"cmn",
|
||||||
|
"cmp",
|
||||||
|
"eor",
|
||||||
|
"ldm",
|
||||||
|
"ldr",
|
||||||
|
"lsl",
|
||||||
|
"lsr",
|
||||||
|
"mla",
|
||||||
|
"mov",
|
||||||
|
"mrs",
|
||||||
|
"msr",
|
||||||
|
"mul",
|
||||||
|
"mvn",
|
||||||
|
"neg",
|
||||||
|
"orr",
|
||||||
|
"ror",
|
||||||
|
"rsb",
|
||||||
|
"rsc",
|
||||||
|
"sbc",
|
||||||
|
"smlal",
|
||||||
|
"smull",
|
||||||
|
"stm",
|
||||||
|
"str",
|
||||||
|
"sub",
|
||||||
|
"swi",
|
||||||
|
"swp",
|
||||||
|
"teq",
|
||||||
|
"tst",
|
||||||
|
"umlal",
|
||||||
|
"umull",
|
||||||
|
|
||||||
|
"ill"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* _armDirectionStrings[] = {
|
||||||
|
"da",
|
||||||
|
"ia",
|
||||||
|
"db",
|
||||||
|
"ib"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* _armAccessTypeStrings[] = {
|
||||||
|
"",
|
||||||
|
"b",
|
||||||
|
"h",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
|
||||||
|
"",
|
||||||
|
"sb",
|
||||||
|
"sh",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
|
||||||
|
"",
|
||||||
|
"bt",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"t",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen) {
|
||||||
|
const char* mnemonic = _armMnemonicStrings[info->mnemonic];
|
||||||
|
int written;
|
||||||
|
int total = 0;
|
||||||
|
const char* cond = "";
|
||||||
|
if (info->condition != ARM_CONDITION_AL && info->condition < ARM_CONDITION_NV) {
|
||||||
|
cond = _armConditions[info->condition];
|
||||||
|
}
|
||||||
|
const char* flags = "";
|
||||||
|
switch (info->mnemonic) {
|
||||||
|
case ARM_MN_LDM:
|
||||||
|
case ARM_MN_STM:
|
||||||
|
flags = _armDirectionStrings[MEMORY_FORMAT_TO_DIRECTION(info->memory.format)];
|
||||||
|
break;
|
||||||
|
case ARM_MN_LDR:
|
||||||
|
case ARM_MN_STR:
|
||||||
|
case ARM_MN_SWP:
|
||||||
|
flags = _armAccessTypeStrings[info->memory.width];
|
||||||
|
break;
|
||||||
|
case ARM_MN_ADD:
|
||||||
|
case ARM_MN_ADC:
|
||||||
|
case ARM_MN_AND:
|
||||||
|
case ARM_MN_BIC:
|
||||||
|
case ARM_MN_EOR:
|
||||||
|
case ARM_MN_MOV:
|
||||||
|
case ARM_MN_MVN:
|
||||||
|
case ARM_MN_ORR:
|
||||||
|
case ARM_MN_RSB:
|
||||||
|
case ARM_MN_RSC:
|
||||||
|
case ARM_MN_SBC:
|
||||||
|
case ARM_MN_SUB:
|
||||||
|
if (info->affectsCPSR) {
|
||||||
|
flags = "s";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
written = snprintf(buffer, blen - 1, "%s%s%s ", mnemonic, cond, flags);
|
||||||
|
ADVANCE(written);
|
||||||
|
|
||||||
|
switch (info->mnemonic) {
|
||||||
|
case ARM_MN_LDM:
|
||||||
|
case ARM_MN_STM:
|
||||||
|
written = _decodeRegister(info->memory.baseReg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
if (info->memory.format & ARM_MEMORY_WRITEBACK) {
|
||||||
|
strncpy(buffer, "!", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
strncpy(buffer, ", ", blen - 1);
|
||||||
|
ADVANCE(2);
|
||||||
|
written = _decodeRegisterList(info->op1.immediate, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
if (info->memory.format & ARM_MEMORY_SPSR_SWAP) {
|
||||||
|
strncpy(buffer, "^", blen - 1);
|
||||||
|
ADVANCE(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARM_MN_B:
|
||||||
|
case ARM_MN_BL:
|
||||||
|
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
|
||||||
|
written = _decodePCRelative(info->op1.immediate, pc, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
|
||||||
|
written = snprintf(buffer, blen - 1, "#%i", info->op1.immediate);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_MEMORY_1) {
|
||||||
|
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_REGISTER_1) {
|
||||||
|
written = _decodeRegister(info->op1.reg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
if (info->op1.reg > ARM_PC) {
|
||||||
|
written = _decodePSR(info->op1.psrBits, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_1) {
|
||||||
|
written = _decodeShift(info->op1, true, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_1) {
|
||||||
|
written = _decodeShift(info->op1, false, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_2) {
|
||||||
|
strncpy(buffer, ", ", blen);
|
||||||
|
ADVANCE(2);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) {
|
||||||
|
written = snprintf(buffer, blen - 1, "#%i", info->op2.immediate);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_MEMORY_2) {
|
||||||
|
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_REGISTER_2) {
|
||||||
|
written = _decodeRegister(info->op2.reg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_2) {
|
||||||
|
written = _decodeShift(info->op2, true, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_2) {
|
||||||
|
written = _decodeShift(info->op2, false, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_3) {
|
||||||
|
strncpy(buffer, ", ", blen - 1);
|
||||||
|
ADVANCE(2);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) {
|
||||||
|
written = snprintf(buffer, blen - 1, "#%i", info->op3.immediate);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_MEMORY_3) {
|
||||||
|
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_REGISTER_3) {
|
||||||
|
written = _decodeRegister(info->op3.reg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_3) {
|
||||||
|
written = _decodeShift(info->op3, true, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_3) {
|
||||||
|
written = _decodeShift(info->op3, false, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_4) {
|
||||||
|
strncpy(buffer, ", ", blen - 1);
|
||||||
|
ADVANCE(2);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) {
|
||||||
|
written = snprintf(buffer, blen - 1, "#%i", info->op4.immediate);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_MEMORY_4) {
|
||||||
|
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_REGISTER_4) {
|
||||||
|
written = _decodeRegister(info->op4.reg, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_4) {
|
||||||
|
written = _decodeShift(info->op4, true, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_4) {
|
||||||
|
written = _decodeShift(info->op4, false, buffer, blen);
|
||||||
|
ADVANCE(written);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[blen - 1] = '\0';
|
||||||
|
return total;
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef ARM_DECODER_H
|
||||||
|
#define ARM_DECODER_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include "arm.h"
|
||||||
|
|
||||||
|
// Bit 0: a register is involved with this operand
|
||||||
|
// Bit 1: an immediate is invovled with this operand
|
||||||
|
// Bit 2: a memory access is invovled with this operand
|
||||||
|
// Bit 3: the destination of this operand is affected by this opcode
|
||||||
|
// Bit 4: this operand is shifted by a register
|
||||||
|
// Bit 5: this operand is shifted by an immediate
|
||||||
|
#define ARM_OPERAND_NONE 0x00000000
|
||||||
|
#define ARM_OPERAND_REGISTER_1 0x00000001
|
||||||
|
#define ARM_OPERAND_IMMEDIATE_1 0x00000002
|
||||||
|
#define ARM_OPERAND_MEMORY_1 0x00000004
|
||||||
|
#define ARM_OPERAND_AFFECTED_1 0x00000008
|
||||||
|
#define ARM_OPERAND_SHIFT_REGISTER_1 0x00000010
|
||||||
|
#define ARM_OPERAND_SHIFT_IMMEDIATE_1 0x00000020
|
||||||
|
#define ARM_OPERAND_1 0x000000FF
|
||||||
|
|
||||||
|
#define ARM_OPERAND_REGISTER_2 0x00000100
|
||||||
|
#define ARM_OPERAND_IMMEDIATE_2 0x00000200
|
||||||
|
#define ARM_OPERAND_MEMORY_2 0x00000400
|
||||||
|
#define ARM_OPERAND_AFFECTED_2 0x00000800
|
||||||
|
#define ARM_OPERAND_SHIFT_REGISTER_2 0x00001000
|
||||||
|
#define ARM_OPERAND_SHIFT_IMMEDIATE_2 0x00002000
|
||||||
|
#define ARM_OPERAND_2 0x0000FF00
|
||||||
|
|
||||||
|
#define ARM_OPERAND_REGISTER_3 0x00010000
|
||||||
|
#define ARM_OPERAND_IMMEDIATE_3 0x00020000
|
||||||
|
#define ARM_OPERAND_MEMORY_3 0x00040000
|
||||||
|
#define ARM_OPERAND_AFFECTED_3 0x00080000
|
||||||
|
#define ARM_OPERAND_SHIFT_REGISTER_3 0x00100000
|
||||||
|
#define ARM_OPERAND_SHIFT_IMMEDIATE_3 0x00200000
|
||||||
|
#define ARM_OPERAND_3 0x00FF0000
|
||||||
|
|
||||||
|
#define ARM_OPERAND_REGISTER_4 0x01000000
|
||||||
|
#define ARM_OPERAND_IMMEDIATE_4 0x02000000
|
||||||
|
#define ARM_OPERAND_MEMORY_4 0x04000000
|
||||||
|
#define ARM_OPERAND_AFFECTED_4 0x08000000
|
||||||
|
#define ARM_OPERAND_SHIFT_REGISTER_4 0x10000000
|
||||||
|
#define ARM_OPERAND_SHIFT_IMMEDIATE_4 0x20000000
|
||||||
|
#define ARM_OPERAND_4 0xFF000000
|
||||||
|
|
||||||
|
#define ARM_OPERAND_MEMORY (ARM_OPERAND_MEMORY_1 | ARM_OPERAND_MEMORY_2 | ARM_OPERAND_MEMORY_3 | ARM_OPERAND_MEMORY_4)
|
||||||
|
|
||||||
|
#define ARM_MEMORY_REGISTER_BASE 0x0001
|
||||||
|
#define ARM_MEMORY_IMMEDIATE_OFFSET 0x0002
|
||||||
|
#define ARM_MEMORY_REGISTER_OFFSET 0x0004
|
||||||
|
#define ARM_MEMORY_SHIFTED_OFFSET 0x0008
|
||||||
|
#define ARM_MEMORY_PRE_INCREMENT 0x0010
|
||||||
|
#define ARM_MEMORY_POST_INCREMENT 0x0020
|
||||||
|
#define ARM_MEMORY_OFFSET_SUBTRACT 0x0040
|
||||||
|
#define ARM_MEMORY_WRITEBACK 0x0080
|
||||||
|
#define ARM_MEMORY_DECREMENT_AFTER 0x0000
|
||||||
|
#define ARM_MEMORY_INCREMENT_AFTER 0x0100
|
||||||
|
#define ARM_MEMORY_DECREMENT_BEFORE 0x0200
|
||||||
|
#define ARM_MEMORY_INCREMENT_BEFORE 0x0300
|
||||||
|
#define ARM_MEMORY_SPSR_SWAP 0x0400
|
||||||
|
|
||||||
|
#define ARM_PSR_C 1
|
||||||
|
#define ARM_PSR_X 2
|
||||||
|
#define ARM_PSR_S 4
|
||||||
|
#define ARM_PSR_F 8
|
||||||
|
#define ARM_PSR_MASK 0xF
|
||||||
|
|
||||||
|
#define MEMORY_FORMAT_TO_DIRECTION(F) (((F) >> 8) & 0x3)
|
||||||
|
|
||||||
|
enum ARMCondition {
|
||||||
|
ARM_CONDITION_EQ = 0x0,
|
||||||
|
ARM_CONDITION_NE = 0x1,
|
||||||
|
ARM_CONDITION_CS = 0x2,
|
||||||
|
ARM_CONDITION_CC = 0x3,
|
||||||
|
ARM_CONDITION_MI = 0x4,
|
||||||
|
ARM_CONDITION_PL = 0x5,
|
||||||
|
ARM_CONDITION_VS = 0x6,
|
||||||
|
ARM_CONDITION_VC = 0x7,
|
||||||
|
ARM_CONDITION_HI = 0x8,
|
||||||
|
ARM_CONDITION_LS = 0x9,
|
||||||
|
ARM_CONDITION_GE = 0xA,
|
||||||
|
ARM_CONDITION_LT = 0xB,
|
||||||
|
ARM_CONDITION_GT = 0xC,
|
||||||
|
ARM_CONDITION_LE = 0xD,
|
||||||
|
ARM_CONDITION_AL = 0xE,
|
||||||
|
ARM_CONDITION_NV = 0xF
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ARMShifterOperation {
|
||||||
|
ARM_SHIFT_NONE = 0,
|
||||||
|
ARM_SHIFT_LSL,
|
||||||
|
ARM_SHIFT_LSR,
|
||||||
|
ARM_SHIFT_ASR,
|
||||||
|
ARM_SHIFT_ROR,
|
||||||
|
ARM_SHIFT_RRX
|
||||||
|
};
|
||||||
|
|
||||||
|
union ARMOperand {
|
||||||
|
struct {
|
||||||
|
uint8_t reg;
|
||||||
|
uint8_t shifterOp;
|
||||||
|
union {
|
||||||
|
uint8_t shifterReg;
|
||||||
|
uint8_t shifterImm;
|
||||||
|
uint8_t psrBits;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
int32_t immediate;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ARMMemoryAccessType {
|
||||||
|
ARM_ACCESS_WORD = 4,
|
||||||
|
ARM_ACCESS_HALFWORD = 2,
|
||||||
|
ARM_ACCESS_SIGNED_HALFWORD = 10,
|
||||||
|
ARM_ACCESS_BYTE = 1,
|
||||||
|
ARM_ACCESS_SIGNED_BYTE = 9,
|
||||||
|
ARM_ACCESS_TRANSLATED_WORD = 20,
|
||||||
|
ARM_ACCESS_TRANSLATED_BYTE = 17
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ARMBranchType {
|
||||||
|
ARM_BRANCH_NONE = 0,
|
||||||
|
ARM_BRANCH = 1,
|
||||||
|
ARM_BRANCH_INDIRECT = 2,
|
||||||
|
ARM_BRANCH_LINKED = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ARMMemoryAccess {
|
||||||
|
uint8_t baseReg;
|
||||||
|
uint8_t width;
|
||||||
|
uint16_t format;
|
||||||
|
union ARMOperand offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ARMMnemonic {
|
||||||
|
ARM_MN_ILL = 0,
|
||||||
|
ARM_MN_ADC,
|
||||||
|
ARM_MN_ADD,
|
||||||
|
ARM_MN_AND,
|
||||||
|
ARM_MN_ASR,
|
||||||
|
ARM_MN_B,
|
||||||
|
ARM_MN_BIC,
|
||||||
|
ARM_MN_BKPT,
|
||||||
|
ARM_MN_BL,
|
||||||
|
ARM_MN_BX,
|
||||||
|
ARM_MN_CMN,
|
||||||
|
ARM_MN_CMP,
|
||||||
|
ARM_MN_EOR,
|
||||||
|
ARM_MN_LDM,
|
||||||
|
ARM_MN_LDR,
|
||||||
|
ARM_MN_LSL,
|
||||||
|
ARM_MN_LSR,
|
||||||
|
ARM_MN_MLA,
|
||||||
|
ARM_MN_MOV,
|
||||||
|
ARM_MN_MRS,
|
||||||
|
ARM_MN_MSR,
|
||||||
|
ARM_MN_MUL,
|
||||||
|
ARM_MN_MVN,
|
||||||
|
ARM_MN_NEG,
|
||||||
|
ARM_MN_ORR,
|
||||||
|
ARM_MN_ROR,
|
||||||
|
ARM_MN_RSB,
|
||||||
|
ARM_MN_RSC,
|
||||||
|
ARM_MN_SBC,
|
||||||
|
ARM_MN_SMLAL,
|
||||||
|
ARM_MN_SMULL,
|
||||||
|
ARM_MN_STM,
|
||||||
|
ARM_MN_STR,
|
||||||
|
ARM_MN_SUB,
|
||||||
|
ARM_MN_SWI,
|
||||||
|
ARM_MN_SWP,
|
||||||
|
ARM_MN_TEQ,
|
||||||
|
ARM_MN_TST,
|
||||||
|
ARM_MN_UMLAL,
|
||||||
|
ARM_MN_UMULL,
|
||||||
|
|
||||||
|
ARM_MN_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARM_CPSR = 16,
|
||||||
|
ARM_SPSR = 17
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ARMInstructionInfo {
|
||||||
|
uint32_t opcode;
|
||||||
|
union ARMOperand op1;
|
||||||
|
union ARMOperand op2;
|
||||||
|
union ARMOperand op3;
|
||||||
|
union ARMOperand op4;
|
||||||
|
struct ARMMemoryAccess memory;
|
||||||
|
int operandFormat;
|
||||||
|
bool traps : 1;
|
||||||
|
bool affectsCPSR : 1;
|
||||||
|
unsigned branchType : 3;
|
||||||
|
unsigned condition : 4;
|
||||||
|
unsigned mnemonic : 6;
|
||||||
|
unsigned iCycles : 3;
|
||||||
|
unsigned cCycles : 4;
|
||||||
|
unsigned sInstructionCycles : 4;
|
||||||
|
unsigned nInstructionCycles : 4;
|
||||||
|
unsigned sDataCycles : 10;
|
||||||
|
unsigned nDataCycles : 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info);
|
||||||
|
int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,335 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef EMITTER_ARM_H
|
||||||
|
#define EMITTER_ARM_H
|
||||||
|
|
||||||
|
#include "emitter-inlines.h"
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_ARM(EMITTER, NAME) \
|
||||||
|
EMITTER ## NAME
|
||||||
|
|
||||||
|
#define DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ALU) \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## I)), \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## I))
|
||||||
|
|
||||||
|
#define DECLARE_ARM_ALU_BLOCK(EMITTER, ALU, EX1, EX2, EX3, EX4) \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, EX1), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _LSR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, EX2), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ASR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, EX3), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ALU ## _ROR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, EX4)
|
||||||
|
|
||||||
|
#define DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, NAME, P, U, W) \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## I ## P ## U ## W)), \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## I ## P ## U ## W))
|
||||||
|
|
||||||
|
#define DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, NAME, P, U, W) \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSL_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSR_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ASR_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ROR_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSL_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _LSR_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ASR_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## _ROR_ ## P ## U ## W), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL)
|
||||||
|
|
||||||
|
#define DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, NAME, MODE, W) \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## MODE ## W)), \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME ## MODE ## W))
|
||||||
|
|
||||||
|
#define DECLARE_ARM_BRANCH_BLOCK(EMITTER, NAME) \
|
||||||
|
DO_256(DECLARE_INSTRUCTION_ARM(EMITTER, NAME))
|
||||||
|
|
||||||
|
// TODO: Support coprocessors
|
||||||
|
#define DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, NAME, P, U, N, W) \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME)), \
|
||||||
|
DO_8(DECLARE_INSTRUCTION_ARM(EMITTER, NAME))
|
||||||
|
|
||||||
|
#define DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, NAME1, NAME2) \
|
||||||
|
DO_8(DO_8(DO_INTERLACE(DECLARE_INSTRUCTION_ARM(EMITTER, NAME1), DECLARE_INSTRUCTION_ARM(EMITTER, NAME2))))
|
||||||
|
|
||||||
|
#define DECLARE_ARM_SWI_BLOCK(EMITTER) \
|
||||||
|
DO_256(DECLARE_INSTRUCTION_ARM(EMITTER, SWI))
|
||||||
|
|
||||||
|
#define DECLARE_ARM_EMITTER_BLOCK(EMITTER) \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, AND, MUL, STRH, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ANDS, MULS, LDRH, LDRSB, LDRSH), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, EOR, MLA, STRH, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, EORS, MLAS, LDRH, LDRSB, LDRSH), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, SUB, ILL, STRHI, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, SUBS, ILL, LDRHI, LDRSBI, LDRSHI), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, RSB, ILL, STRHI, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, RSBS, ILL, LDRHI, LDRSBI, LDRSHI), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ADD, UMULL, STRHU, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ADDS, UMULLS, LDRHU, LDRSBU, LDRSHU), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ADC, UMLAL, STRHU, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ADCS, UMLALS, LDRHU, LDRSBU, LDRSHU), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, SBC, SMULL, STRHIU, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, SBCS, SMULLS, LDRHIU, LDRSBIU, LDRSHIU), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, RSC, SMLAL, STRHIU, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, RSCS, SMLALS, LDRHIU, LDRSBIU, LDRSHIU), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, MRS), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, SWP), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, STRHP), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, TST, ILL, LDRHP, LDRSBP, LDRSHP), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, MSR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, BX), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, BKPT), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, STRHPW), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, TEQ, ILL, LDRHPW, LDRSBPW, LDRSHPW), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, MRSR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, SWPB), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, STRHIP), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, CMP, ILL, LDRHIP, LDRSBIP, LDRSHIP), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, MSRR), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, STRHIPW), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_INSTRUCTION_ARM(EMITTER, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, CMN, ILL, LDRHIPW, LDRSBIPW, LDRSHIPW), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ORR, SMLAL, STRHPU, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, ORRS, SMLALS, LDRHPU, LDRSBPU, LDRSHPU), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, MOV, SMLAL, STRHPUW, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, MOVS, SMLALS, LDRHPUW, LDRSBPUW, LDRSHPUW), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, BIC, SMLAL, STRHIPU, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, BICS, SMLALS, LDRHIPU, LDRSBIPU, LDRSHIPU), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, MVN, SMLAL, STRHIPUW, ILL, ILL), \
|
||||||
|
DECLARE_ARM_ALU_BLOCK(EMITTER, MVNS, SMLALS, LDRHIPUW, LDRSBIPUW, LDRSHIPUW), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, AND), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ANDS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, EOR), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, EORS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SUB), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SUBS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSB), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSBS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADD), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADDS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADC), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ADCS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SBC), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, SBCS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSC), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, RSCS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TST), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TST), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MSR), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, TEQ), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMP), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMP), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MSRR), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, CMN), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ORR), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, ORRS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MOV), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MOVS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, BIC), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, BICS), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MVN), \
|
||||||
|
DECLARE_ARM_ALU_IMMEDIATE_BLOCK(EMITTER, MVNS), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRBT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRBT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRBT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRBT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STR, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDR, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, STRB, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_IMMEDIATE_BLOCK(EMITTER, LDRB, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRBT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRBT, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRBT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRBT, , U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STR, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDR, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, U, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, STRB, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_BLOCK(EMITTER, LDRB, P, U, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IA, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IA, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, DB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, DB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, DB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, DB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STM, IB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDM, IB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IB, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, STMS, IB, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_MULTIPLE_BLOCK(EMITTER, LDMS, IB, W), \
|
||||||
|
DECLARE_ARM_BRANCH_BLOCK(EMITTER, B), \
|
||||||
|
DECLARE_ARM_BRANCH_BLOCK(EMITTER, BL), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , , N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , , N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, , U, N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, , U, N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , , ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , , W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, , N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, , N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, ), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, STC, P, U, N, W), \
|
||||||
|
DECLARE_ARM_LOAD_STORE_COPROCESSOR_BLOCK(EMITTER, LDC, P, U, N, W), \
|
||||||
|
DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, CDP, MCR), \
|
||||||
|
DECLARE_ARM_COPROCESSOR_BLOCK(EMITTER, CDP, MRC), \
|
||||||
|
DECLARE_ARM_SWI_BLOCK(EMITTER)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef EMITTER_INLINES_H
|
||||||
|
#define EMITTER_INLINES_H
|
||||||
|
|
||||||
|
#define DO_4(DIRECTIVE) \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE
|
||||||
|
|
||||||
|
#define DO_8(DIRECTIVE) \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE, \
|
||||||
|
DIRECTIVE
|
||||||
|
|
||||||
|
#define DO_256(DIRECTIVE) \
|
||||||
|
DO_4(DO_8(DO_8(DIRECTIVE)))
|
||||||
|
|
||||||
|
#define DO_INTERLACE(LEFT, RIGHT) \
|
||||||
|
LEFT, \
|
||||||
|
RIGHT
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,609 @@
|
||||||
|
#include "emu.h"
|
||||||
|
#include "wind.h"
|
||||||
|
#include "wind_hw.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Emu::Emu() {
|
||||||
|
configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t Emu::getRTC() {
|
||||||
|
return time(nullptr) - 946684800;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t Emu::readReg8(uint32_t reg) {
|
||||||
|
if ((reg & 0xF00) == 0x600) {
|
||||||
|
return uart1.readReg8(reg & 0xFF);
|
||||||
|
} else if ((reg & 0xF00) == 0x700) {
|
||||||
|
return uart2.readReg8(reg & 0xFF);
|
||||||
|
} else if (reg == TC1CTRL) {
|
||||||
|
return tc1.config;
|
||||||
|
} else if (reg == TC2CTRL) {
|
||||||
|
return tc2.config;
|
||||||
|
} else if (reg == PADR) {
|
||||||
|
return readKeyboard();
|
||||||
|
} else if (reg == PBDR) {
|
||||||
|
return (portValues >> 16) & 0xFF;
|
||||||
|
} else if (reg == PCDR) {
|
||||||
|
return (portValues >> 8) & 0xFF;
|
||||||
|
} else if (reg == PDDR) {
|
||||||
|
return portValues & 0xFF;
|
||||||
|
} else if (reg == PADDR) {
|
||||||
|
return (portDirections >> 24) & 0xFF;
|
||||||
|
} else if (reg == PBDDR) {
|
||||||
|
return (portDirections >> 16) & 0xFF;
|
||||||
|
} else if (reg == PCDDR) {
|
||||||
|
return (portDirections >> 8) & 0xFF;
|
||||||
|
} else if (reg == PDDDR) {
|
||||||
|
return portDirections & 0xFF;
|
||||||
|
} else {
|
||||||
|
// printf("RegRead8 unknown:: pc=%08x lr=%08x reg=%03x\n", cpu.gprs[ARM_PC]-4, cpu.gprs[ARM_LR], reg);
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t Emu::readReg32(uint32_t reg) {
|
||||||
|
if (reg == LCDCTL) {
|
||||||
|
printf("LCD control read pc=%08x lr=%08x !!!\n", cpu.gprs[ARM_PC], cpu.gprs[ARM_LR]);
|
||||||
|
return lcdControl;
|
||||||
|
} else if (reg == LCDST) {
|
||||||
|
printf("LCD state read pc=%08x lr=%08x !!!\n", cpu.gprs[ARM_PC], cpu.gprs[ARM_LR]);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
} else if (reg == PWRSR) {
|
||||||
|
// printf("!!! PWRSR read pc=%08x lr=%08x !!!\n", cpu.gprs[ARM_PC], cpu.gprs[ARM_LR]);
|
||||||
|
return pwrsr;
|
||||||
|
} else if (reg == INTSR) {
|
||||||
|
return pendingInterrupts & interruptMask;
|
||||||
|
} else if (reg == INTRSR) {
|
||||||
|
return pendingInterrupts;
|
||||||
|
} else if (reg == INTENS) {
|
||||||
|
return interruptMask;
|
||||||
|
} else if ((reg & 0xF00) == 0x600) {
|
||||||
|
return uart1.readReg32(reg & 0xFF);
|
||||||
|
} else if ((reg & 0xF00) == 0x700) {
|
||||||
|
return uart2.readReg32(reg & 0xFF);
|
||||||
|
} else if (reg == TC1VAL) {
|
||||||
|
return tc1.value;
|
||||||
|
} else if (reg == TC2VAL) {
|
||||||
|
return tc2.value;
|
||||||
|
} else if (reg == SSSR) {
|
||||||
|
// printf("!!! SSSR kludge! !!!\n");
|
||||||
|
return 0;
|
||||||
|
} else if (reg == RTCDRL) {
|
||||||
|
// uint16_t v = getRTC() & 0xFFFF;
|
||||||
|
uint16_t v = rtc & 0xFFFF;
|
||||||
|
// printf("RTCDRL: %04x\n", v);
|
||||||
|
return v;
|
||||||
|
} else if (reg == RTCDRU) {
|
||||||
|
// uint16_t v = getRTC() >> 16;
|
||||||
|
uint16_t v = rtc >> 16;
|
||||||
|
// printf("RTCDRU: %04x\n", v);
|
||||||
|
return v;
|
||||||
|
} else if (reg == KSCAN) {
|
||||||
|
return kScan;
|
||||||
|
} else {
|
||||||
|
// printf("RegRead32 unknown:: pc=%08x lr=%08x reg=%03x\n", cpu.gprs[ARM_PC]-4, cpu.gprs[ARM_LR], reg);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
|
if ((reg & 0xF00) == 0x600) {
|
||||||
|
uart1.writeReg8(reg & 0xFF, value);
|
||||||
|
} else if ((reg & 0xF00) == 0x700) {
|
||||||
|
uart2.writeReg8(reg & 0xFF, value);
|
||||||
|
} else if (reg == TC1CTRL) {
|
||||||
|
tc1.config = value;
|
||||||
|
} else if (reg == TC2CTRL) {
|
||||||
|
tc2.config = value;
|
||||||
|
} else if (reg == PADR) {
|
||||||
|
uint32_t oldPorts = portValues;
|
||||||
|
portValues &= 0x00FFFFFF;
|
||||||
|
portValues |= (uint32_t)value << 24;
|
||||||
|
diffPorts(oldPorts, portValues);
|
||||||
|
} else if (reg == PBDR) {
|
||||||
|
uint32_t oldPorts = portValues;
|
||||||
|
portValues &= 0xFF00FFFF;
|
||||||
|
portValues |= (uint32_t)value << 16;
|
||||||
|
diffPorts(oldPorts, portValues);
|
||||||
|
} else if (reg == PCDR) {
|
||||||
|
uint32_t oldPorts = portValues;
|
||||||
|
portValues &= 0xFFFF00FF;
|
||||||
|
portValues |= (uint32_t)value << 8;
|
||||||
|
diffPorts(oldPorts, portValues);
|
||||||
|
} else if (reg == PDDR) {
|
||||||
|
uint32_t oldPorts = portValues;
|
||||||
|
portValues &= 0xFFFFFF00;
|
||||||
|
portValues |= (uint32_t)value;
|
||||||
|
diffPorts(oldPorts, portValues);
|
||||||
|
} else if (reg == PADDR) {
|
||||||
|
portDirections &= 0x00FFFFFF;
|
||||||
|
portDirections |= (uint32_t)value << 24;
|
||||||
|
} else if (reg == PBDDR) {
|
||||||
|
portDirections &= 0xFF00FFFF;
|
||||||
|
portDirections |= (uint32_t)value << 16;
|
||||||
|
} else if (reg == PCDDR) {
|
||||||
|
portDirections &= 0xFFFF00FF;
|
||||||
|
portDirections |= (uint32_t)value << 8;
|
||||||
|
} else if (reg == PDDDR) {
|
||||||
|
portDirections &= 0xFFFFFF00;
|
||||||
|
portDirections |= (uint32_t)value;
|
||||||
|
} else if (reg == KSCAN) {
|
||||||
|
kScan = value;
|
||||||
|
} else {
|
||||||
|
// printf("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x\n", cpu.gprs[ARM_PC]-4, reg, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Emu::writeReg32(uint32_t reg, uint32_t value) {
|
||||||
|
if (reg == LCDCTL) {
|
||||||
|
printf("LCD: ctl write %08x\n", value);
|
||||||
|
lcdControl = value;
|
||||||
|
} else if (reg == LCD_DBAR1) {
|
||||||
|
printf("LCD: address write %08x\n", value);
|
||||||
|
lcdAddress = value;
|
||||||
|
} else if (reg == LCDT0) {
|
||||||
|
printf("LCD: horz timing write %08x\n", value);
|
||||||
|
} else if (reg == LCDT1) {
|
||||||
|
printf("LCD: vert timing write %08x\n", value);
|
||||||
|
} else if (reg == LCDT2) {
|
||||||
|
printf("LCD: clocks write %08x\n", value);
|
||||||
|
} else if (reg == INTENS) {
|
||||||
|
// diffInterrupts(interruptMask, interruptMask | value);
|
||||||
|
interruptMask |= value;
|
||||||
|
} else if (reg == INTENC) {
|
||||||
|
// diffInterrupts(interruptMask, interruptMask &~ value);
|
||||||
|
interruptMask &= ~value;
|
||||||
|
} else if (reg == HALT) {
|
||||||
|
cpu.halted = true;
|
||||||
|
// BLEOI = 0x410,
|
||||||
|
// MCEOI = 0x414,
|
||||||
|
} else if (reg == TEOI) {
|
||||||
|
pendingInterrupts &= ~(1 << TINT);
|
||||||
|
// TEOI = 0x418,
|
||||||
|
// STFCLR = 0x41C,
|
||||||
|
// E2EOI = 0x420,
|
||||||
|
} else if ((reg & 0xF00) == 0x600) {
|
||||||
|
uart1.writeReg32(reg & 0xFF, value);
|
||||||
|
} else if ((reg & 0xF00) == 0x700) {
|
||||||
|
uart2.writeReg32(reg & 0xFF, value);
|
||||||
|
} else if (reg == TC1LOAD) {
|
||||||
|
tc1.load(value);
|
||||||
|
} else if (reg == TC1EOI) {
|
||||||
|
pendingInterrupts &= ~(1 << TC1OI);
|
||||||
|
} else if (reg == TC2LOAD) {
|
||||||
|
tc2.load(value);
|
||||||
|
} else if (reg == TC2EOI) {
|
||||||
|
pendingInterrupts &= ~(1 << TC2OI);
|
||||||
|
} else {
|
||||||
|
// printf("RegWrite32 unknown:: pc=%08x reg=%03x value=%08x\n", cpu.gprs[ARM_PC]-4, reg, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Emu::readPhys8(uint32_t physAddress) {
|
||||||
|
uint32_t result = 0xFF;
|
||||||
|
uint8_t region = physAddress >> 28;
|
||||||
|
if (region == 0)
|
||||||
|
result = ROM[physAddress & 0xFFFFFF];
|
||||||
|
else if (region == 8 && physAddress <= 0x80000FFF)
|
||||||
|
result = readReg8(physAddress & 0xFFF);
|
||||||
|
else if (region == 0xC)
|
||||||
|
result = MemoryBlockC[physAddress & MemoryBlockCMask];
|
||||||
|
else if (region == 0xD)
|
||||||
|
result = MemoryBlockD[physAddress & MemoryBlockDMask];
|
||||||
|
// else
|
||||||
|
// printf("<%08x> unmapped read8 addr p:%08x\n", cpu.gprs[ARM_PC] - 4, physAddress);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
uint32_t Emu::readPhys16(uint32_t physAddress) {
|
||||||
|
uint32_t result = 0xFFFFFFFF;
|
||||||
|
uint8_t region = physAddress >> 28;
|
||||||
|
if (region == 0)
|
||||||
|
LOAD_16LE(result, physAddress & 0xFFFFFF, ROM);
|
||||||
|
else if (region == 0xC)
|
||||||
|
LOAD_16LE(result, physAddress & MemoryBlockCMask, MemoryBlockC);
|
||||||
|
else if (region == 0xD)
|
||||||
|
LOAD_16LE(result, physAddress & MemoryBlockDMask, MemoryBlockD);
|
||||||
|
// else
|
||||||
|
// printf("<%08x> unmapped read16 addr p:%08x\n", cpu.gprs[ARM_PC] - 4, physAddress);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
uint32_t Emu::readPhys32(uint32_t physAddress) {
|
||||||
|
uint32_t result = 0xFFFFFFFF;
|
||||||
|
uint8_t region = physAddress >> 28;
|
||||||
|
if (region == 0)
|
||||||
|
LOAD_32LE(result, physAddress & 0xFFFFFF, ROM);
|
||||||
|
else if (region == 8 && physAddress <= 0x80000FFF)
|
||||||
|
result = readReg32(physAddress & 0xFFF);
|
||||||
|
else if (region == 0xC)
|
||||||
|
LOAD_32LE(result, physAddress & MemoryBlockCMask, MemoryBlockC);
|
||||||
|
else if (region == 0xD)
|
||||||
|
LOAD_32LE(result, physAddress & MemoryBlockDMask, MemoryBlockD);
|
||||||
|
// else
|
||||||
|
// printf("<%08x> unmapped read32 addr p:%08x\n", cpu.gprs[ARM_PC] - 4, physAddress);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::writePhys8(uint32_t physAddress, uint8_t value) {
|
||||||
|
uint8_t region = physAddress >> 28;
|
||||||
|
if (region == 0xC)
|
||||||
|
MemoryBlockC[physAddress & MemoryBlockCMask] = (uint8_t)value;
|
||||||
|
else if (region == 0xD)
|
||||||
|
MemoryBlockD[physAddress & MemoryBlockDMask] = (uint8_t)value;
|
||||||
|
else if (region == 8 && physAddress <= 0x80000FFF)
|
||||||
|
writeReg8(physAddress & 0xFFF, value);
|
||||||
|
// else
|
||||||
|
// printf("<%08x> unmapped write8 addr p:%08x :: %02x\n", cpu.gprs[ARM_PC] - 4, physAddress, value);
|
||||||
|
}
|
||||||
|
void Emu::writePhys16(uint32_t physAddress, uint16_t value) {
|
||||||
|
uint8_t region = physAddress >> 28;
|
||||||
|
if (region == 0xC)
|
||||||
|
STORE_16LE(value, physAddress & MemoryBlockCMask, MemoryBlockC);
|
||||||
|
else if (region == 0xD)
|
||||||
|
STORE_16LE(value, physAddress & MemoryBlockDMask, MemoryBlockD);
|
||||||
|
// else
|
||||||
|
// printf("<%08x> unmapped write16 addr p:%08x :: %04x\n", cpu.gprs[ARM_PC] - 4, physAddress, value);
|
||||||
|
}
|
||||||
|
void Emu::writePhys32(uint32_t physAddress, uint32_t value) {
|
||||||
|
uint8_t region = physAddress >> 28;
|
||||||
|
if (region == 0xC)
|
||||||
|
STORE_32LE(value, physAddress & MemoryBlockCMask, MemoryBlockC);
|
||||||
|
else if (region == 0xD)
|
||||||
|
STORE_32LE(value, physAddress & MemoryBlockDMask, MemoryBlockD);
|
||||||
|
else if (region == 8 && physAddress <= 0x80000FFF)
|
||||||
|
writeReg32(physAddress & 0xFFF, value);
|
||||||
|
// else
|
||||||
|
// printf("<%08x> unmapped write32 addr p:%08x :: %08x\n", cpu.gprs[ARM_PC] - 4, physAddress, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Emu::virtToPhys(uint32_t virtAddress) {
|
||||||
|
if (!isMMU())
|
||||||
|
return virtAddress;
|
||||||
|
|
||||||
|
// find the TTB
|
||||||
|
uint32_t ttbEntryAddr = translationTableBase & 0xFFFFC000;
|
||||||
|
ttbEntryAddr |= ((virtAddress & 0xFFF00000) >> 18);
|
||||||
|
uint32_t ttbEntry = readPhys32(ttbEntryAddr);
|
||||||
|
|
||||||
|
if ((ttbEntry & 3) == 1) {
|
||||||
|
// Page
|
||||||
|
uint32_t pageTableAddr = ttbEntry & 0xFFFFFC00;
|
||||||
|
pageTableAddr |= ((virtAddress & 0x000FF000) >> 10);
|
||||||
|
uint32_t pageTableEntry = readPhys32(pageTableAddr);
|
||||||
|
if ((pageTableEntry & 3) == 1) {
|
||||||
|
// Large Page
|
||||||
|
uint32_t lpBaseAddr = pageTableEntry & 0xFFFF0000;
|
||||||
|
return lpBaseAddr | (virtAddress & 0x0000FFFF);
|
||||||
|
} else if ((pageTableEntry & 3) == 2) {
|
||||||
|
// Small Page
|
||||||
|
uint32_t lpBaseAddr = pageTableEntry & 0xFFFFF000;
|
||||||
|
return lpBaseAddr | (virtAddress & 0x00000FFF);
|
||||||
|
} else {
|
||||||
|
// Fault/Reserved
|
||||||
|
// TODO: should raise Abort here?
|
||||||
|
printf("!!! lv2 bad entry=%d vaddr=%08x !!!\n", pageTableEntry & 3, virtAddress);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
} else if ((ttbEntry & 3) == 2) {
|
||||||
|
// Section
|
||||||
|
uint32_t sectBaseAddr = ttbEntry & 0xFFF00000;
|
||||||
|
return sectBaseAddr | (virtAddress & 0x000FFFFF);
|
||||||
|
} else {
|
||||||
|
// Fault/Reserved
|
||||||
|
// TODO: should raise Abort here?
|
||||||
|
printf("!!! lv1 bad entry=%d vaddr=%08x !!!\n", ttbEntry & 3, virtAddress);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Emu::configure() {
|
||||||
|
if (configured) return;
|
||||||
|
configured = true;
|
||||||
|
|
||||||
|
uart1.cpu = &cpu;
|
||||||
|
uart2.cpu = &cpu;
|
||||||
|
memset(&tc1, 0, sizeof(tc1));
|
||||||
|
memset(&tc2, 0, sizeof(tc1));
|
||||||
|
cpu.owner = this;
|
||||||
|
|
||||||
|
nextTickAt = TICK_INTERVAL;
|
||||||
|
rtc = getRTC();
|
||||||
|
|
||||||
|
configureMemoryBindings();
|
||||||
|
configureCpuHandlers();
|
||||||
|
|
||||||
|
ARMReset(&cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::configureMemoryBindings() {
|
||||||
|
cpu.memory.load8 = [](struct ARMCore *cpu, uint32_t address, int *) {
|
||||||
|
return ((Emu *)cpu->owner)->readVirt8(address);
|
||||||
|
};
|
||||||
|
cpu.memory.load16 = [](struct ARMCore *cpu, uint32_t address, int *) {
|
||||||
|
return ((Emu *)cpu->owner)->readVirt16(address);
|
||||||
|
};
|
||||||
|
cpu.memory.load32 = [](struct ARMCore *cpu, uint32_t address, int *) {
|
||||||
|
return ((Emu *)cpu->owner)->readVirt32(address);
|
||||||
|
};
|
||||||
|
cpu.memory.loadMultiple = [](struct ARMCore *cpu, uint32_t address, int mask, enum LSMDirection direction, int *cycleCounter) {
|
||||||
|
uint32_t value;
|
||||||
|
int i, offset = 4, popcount = 0;
|
||||||
|
|
||||||
|
if (direction & LSM_D) {
|
||||||
|
offset = -4;
|
||||||
|
popcount = popcount32(mask);
|
||||||
|
address -= (popcount << 2) - 4;
|
||||||
|
}
|
||||||
|
if (direction & LSM_B)
|
||||||
|
address += offset;
|
||||||
|
|
||||||
|
if (!mask) {
|
||||||
|
value = cpu->memory.load32(cpu, address, cycleCounter);
|
||||||
|
cpu->gprs[ARM_PC] = value;
|
||||||
|
address += 64;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (mask & (1 << i)) {
|
||||||
|
value = cpu->memory.load32(cpu, address, cycleCounter);
|
||||||
|
cpu->gprs[i] = value;
|
||||||
|
address += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction & LSM_B)
|
||||||
|
address -= offset;
|
||||||
|
if (direction & LSM_D)
|
||||||
|
address -= (popcount << 2) + 4;
|
||||||
|
|
||||||
|
return address;
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu.memory.store8 = [](struct ARMCore *cpu, uint32_t address, int8_t value, int *) {
|
||||||
|
((Emu *)cpu->owner)->writeVirt8(address, value);
|
||||||
|
};
|
||||||
|
cpu.memory.store16 = [](struct ARMCore *cpu, uint32_t address, int16_t value, int *) {
|
||||||
|
((Emu *)cpu->owner)->writeVirt16(address, value);
|
||||||
|
};
|
||||||
|
cpu.memory.store32 = [](struct ARMCore *cpu, uint32_t address, int32_t value, int *) {
|
||||||
|
((Emu *)cpu->owner)->writeVirt32(address, value);
|
||||||
|
};
|
||||||
|
cpu.memory.storeMultiple = [](struct ARMCore *cpu, uint32_t address, int mask, enum LSMDirection direction, int *cycleCounter) {
|
||||||
|
uint32_t value, oldValue;
|
||||||
|
int i, offset = 4, popcount = 0;
|
||||||
|
|
||||||
|
if (direction & LSM_D) {
|
||||||
|
offset = -4;
|
||||||
|
popcount = popcount32(mask);
|
||||||
|
address -= (popcount << 2) - 4;
|
||||||
|
}
|
||||||
|
if (direction & LSM_B)
|
||||||
|
address += offset;
|
||||||
|
|
||||||
|
if (!mask) {
|
||||||
|
value = cpu->gprs[ARM_PC] + 4;
|
||||||
|
cpu->memory.store32(cpu, address, value, cycleCounter);
|
||||||
|
address += 64;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (mask & (1 << i)) {
|
||||||
|
value = cpu->gprs[i];
|
||||||
|
if (i == ARM_PC) value += 4;
|
||||||
|
cpu->memory.store32(cpu, address, value, cycleCounter);
|
||||||
|
address += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction & LSM_B)
|
||||||
|
address -= offset;
|
||||||
|
if (direction & LSM_D)
|
||||||
|
address -= (popcount << 2) + 4;
|
||||||
|
|
||||||
|
return address;
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu.memory.activeSeqCycles32 = 0;
|
||||||
|
cpu.memory.activeNonseqCycles32 = 0;
|
||||||
|
cpu.memory.stall = [](struct ARMCore *cpu, int32_t wait) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::configureCpuHandlers() {
|
||||||
|
cpu.irqh.reset = [](struct ARMCore *cpu) {
|
||||||
|
printf("reset...\n");
|
||||||
|
};
|
||||||
|
cpu.irqh.processEvents = [](struct ARMCore *cpu) {
|
||||||
|
// printf("processEvents...\n");
|
||||||
|
};
|
||||||
|
cpu.irqh.swi32 = [](struct ARMCore *cpu, int immediate) {
|
||||||
|
ARMRaiseSWI(cpu);
|
||||||
|
};
|
||||||
|
cpu.irqh.hitIllegal = [](struct ARMCore *cpu, uint32_t opcode) {
|
||||||
|
printf("hitIllegal... %08x\n", opcode);
|
||||||
|
};
|
||||||
|
cpu.irqh.bkpt32 = [](struct ARMCore *cpu, int immediate) {
|
||||||
|
printf("bkpt32... %08x\n", immediate);
|
||||||
|
};
|
||||||
|
cpu.irqh.readCPSR = [](struct ARMCore *cpu) {
|
||||||
|
// printf("readCPSR...\n");
|
||||||
|
// printf("at %08x our priv became %s\n", cpu->gprs[ARM_PC]-4, privname(cpu));
|
||||||
|
};
|
||||||
|
cpu.irqh.hitStub = [](struct ARMCore *cpu, uint32_t opcode) {
|
||||||
|
Emu *emu = (Emu *)cpu->owner;
|
||||||
|
if ((opcode & 0x0F100F10) == 0x0E100F10) {
|
||||||
|
// coprocessor read
|
||||||
|
int cpReg = (opcode & 0x000F0000) >> 16;
|
||||||
|
int armReg = (opcode & 0x0000F000) >> 12;
|
||||||
|
if (cpReg == 0)
|
||||||
|
cpu->gprs[armReg] = 0x41807100; //5mx device id
|
||||||
|
} else if ((opcode & 0x0F100F10) == 0x0E000F10) {
|
||||||
|
// coprocessor write
|
||||||
|
int cpReg = (opcode & 0x000F0000) >> 16;
|
||||||
|
int armReg = (opcode & 0x0000F000) >> 12;
|
||||||
|
if (cpReg == 1) {
|
||||||
|
emu->controlReg = cpu->gprs[armReg];
|
||||||
|
printf("mmu is now %s\n", emu->isMMU() ? "on" : "off");
|
||||||
|
} else if (cpReg == 2) {
|
||||||
|
emu->translationTableBase = cpu->gprs[armReg];
|
||||||
|
} else if (cpReg == 3) {
|
||||||
|
emu->domainAccessControl = cpu->gprs[armReg];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("hitStub... %08x\n", opcode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::loadROM(const char *path) {
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
fread(ROM, 1, sizeof(ROM), f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::executeUntil(int64_t cycles) {
|
||||||
|
while (!asleep && cpu.cycles <= cycles) {
|
||||||
|
if (cpu.cycles >= nextTickAt) {
|
||||||
|
// increment RTCDIV
|
||||||
|
if ((pwrsr & 0x3F) == 0x3F) {
|
||||||
|
rtc++;
|
||||||
|
pwrsr &= ~0x3F;
|
||||||
|
} else {
|
||||||
|
pwrsr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTickAt += TICK_INTERVAL;
|
||||||
|
pendingInterrupts |= (1<<TINT);
|
||||||
|
}
|
||||||
|
if (tc1.tick(cpu.cycles))
|
||||||
|
pendingInterrupts |= (1<<TC1OI);
|
||||||
|
if (tc2.tick(cpu.cycles))
|
||||||
|
pendingInterrupts |= (1<<TC2OI);
|
||||||
|
|
||||||
|
if ((pendingInterrupts & interruptMask & FIQ_INTERRUPTS) != 0)
|
||||||
|
ARMRaiseFIQ(&cpu);
|
||||||
|
if ((pendingInterrupts & interruptMask & IRQ_INTERRUPTS) != 0)
|
||||||
|
ARMRaiseIRQ(&cpu);
|
||||||
|
|
||||||
|
// if (cpu.cycles >= 30000000) {
|
||||||
|
// static bool lcdtest = false;
|
||||||
|
// if (!lcdtest) {
|
||||||
|
// printf("lcdtest\n");
|
||||||
|
// pendingInterrupts |= (1<<LCDINT);
|
||||||
|
// lcdtest = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// what's running?
|
||||||
|
if (cpu.halted) {
|
||||||
|
// keep the clock moving
|
||||||
|
cpu.cycles++;
|
||||||
|
} else {
|
||||||
|
uint32_t pc = cpu.gprs[ARM_PC] - 4;
|
||||||
|
uint32_t phys_pc = virtToPhys(pc);
|
||||||
|
debugPC(phys_pc);
|
||||||
|
ARMRun(&cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::dumpRAM(const char *path) {
|
||||||
|
FILE *f = fopen(path, "wb");
|
||||||
|
fwrite(MemoryBlockC, 1, sizeof(MemoryBlockC), f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Emu::printRegs() {
|
||||||
|
printf("R00:%08x R01:%08x R02:%08x R03:%08x\n", cpu.gprs[0], cpu.gprs[1], cpu.gprs[2], cpu.gprs[3]);
|
||||||
|
printf("R04:%08x R05:%08x R06:%08x R07:%08x\n", cpu.gprs[4], cpu.gprs[5], cpu.gprs[6], cpu.gprs[7]);
|
||||||
|
printf("R08:%08x R09:%08x R10:%08x R11:%08x\n", cpu.gprs[8], cpu.gprs[9], cpu.gprs[10], cpu.gprs[11]);
|
||||||
|
printf("R12:%08x R13:%08x R14:%08x R15:%08x\n", cpu.gprs[12], cpu.gprs[13], cpu.gprs[14], cpu.gprs[15]);
|
||||||
|
printf("cpsr=%08x spsr=%08x\n", cpu.cpsr.packed, cpu.spsr.packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Emu::identifyObjectCon(uint32_t ptr) {
|
||||||
|
if (ptr == readVirt32(0x80000980)) return "process";
|
||||||
|
if (ptr == readVirt32(0x80000984)) return "thread";
|
||||||
|
if (ptr == readVirt32(0x80000988)) return "chunk";
|
||||||
|
// if (ptr == readVirt32(0x8000098C)) return "semaphore";
|
||||||
|
// if (ptr == readVirt32(0x80000990)) return "mutex";
|
||||||
|
if (ptr == readVirt32(0x80000994)) return "logicaldevice";
|
||||||
|
if (ptr == readVirt32(0x80000998)) return "physicaldevice";
|
||||||
|
if (ptr == readVirt32(0x8000099C)) return "channel";
|
||||||
|
if (ptr == readVirt32(0x800009A0)) return "server";
|
||||||
|
// if (ptr == readVirt32(0x800009A4)) return "unk9A4"; // name always null
|
||||||
|
if (ptr == readVirt32(0x800009AC)) return "library";
|
||||||
|
// if (ptr == readVirt32(0x800009B0)) return "unk9B0"; // name always null
|
||||||
|
// if (ptr == readVirt32(0x800009B4)) return "unk9B4"; // name always null
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::fetchStr(uint32_t str, char *buf) {
|
||||||
|
if (str == 0) {
|
||||||
|
strcpy(buf, "<NULL>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int size = readVirt32(str);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
buf[i] = readVirt8(str + 4 + i);
|
||||||
|
}
|
||||||
|
buf[size] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::fetchName(uint32_t obj, char *buf) {
|
||||||
|
fetchStr(readVirt32(obj + 0x10), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::fetchProcessFilename(uint32_t obj, char *buf) {
|
||||||
|
fetchStr(readVirt32(obj + 0x3C), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emu::debugPC(uint32_t pc) {
|
||||||
|
char objName[1000];
|
||||||
|
if (pc == 0x2CBC4) {
|
||||||
|
// CObjectCon::AddL()
|
||||||
|
uint32_t container = cpu.gprs[0];
|
||||||
|
uint32_t obj = cpu.gprs[1];
|
||||||
|
const char *wut = identifyObjectCon(container);
|
||||||
|
if (wut) {
|
||||||
|
fetchName(obj, objName);
|
||||||
|
printf("OBJS: added %s at %08x <%s>", wut, obj, objName);
|
||||||
|
if (strcmp(wut, "process") == 0) {
|
||||||
|
fetchProcessFilename(obj, objName);
|
||||||
|
printf(" <%s>", objName);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const uint8_t *Emu::getLCDBuffer() const {
|
||||||
|
if ((lcdAddress >> 28) == 0xC)
|
||||||
|
return &MemoryBlockC[lcdAddress & MemoryBlockCMask];
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t Emu::readKeyboard() {
|
||||||
|
uint8_t val = 0;
|
||||||
|
if (kScan & 8) {
|
||||||
|
// Select one keyboard
|
||||||
|
int whichColumn = kScan & 7;
|
||||||
|
for (int i = 0; i < 7; i++)
|
||||||
|
if (keyboardKeys[whichColumn * 7 + i])
|
||||||
|
val |= (1 << i);
|
||||||
|
} else if (kScan == 0) {
|
||||||
|
// Report all columns combined
|
||||||
|
// EPOC's keyboard driver relies on this...
|
||||||
|
for (int i = 0; i < 8*7; i++)
|
||||||
|
if (keyboardKeys[i])
|
||||||
|
val |= (1 << (i % 7));
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
#pragma once
|
||||||
|
#include "arm.h"
|
||||||
|
#include "wind_hw.h"
|
||||||
|
|
||||||
|
class Emu {
|
||||||
|
uint8_t ROM[0x1000000];
|
||||||
|
uint8_t MemoryBlockC[0x800000];
|
||||||
|
enum { MemoryBlockCMask = 0x7FFFFF };
|
||||||
|
uint8_t MemoryBlockD[0x800000];
|
||||||
|
enum { MemoryBlockDMask = 0x7FFFFF };
|
||||||
|
|
||||||
|
uint32_t controlReg;
|
||||||
|
uint32_t translationTableBase;
|
||||||
|
uint32_t domainAccessControl;
|
||||||
|
uint16_t pendingInterrupts = 0;
|
||||||
|
uint16_t interruptMask = 0;
|
||||||
|
uint32_t portValues = 0;
|
||||||
|
uint32_t portDirections = 0;
|
||||||
|
uint32_t pwrsr = 0x00002000; // cold start flag
|
||||||
|
uint32_t lcdControl = 0;
|
||||||
|
uint32_t lcdAddress = 0;
|
||||||
|
uint32_t kScan = 0;
|
||||||
|
uint32_t rtc = 0;
|
||||||
|
|
||||||
|
int64_t nextTickAt = 0;
|
||||||
|
Timer tc1, tc2;
|
||||||
|
UART uart1, uart2;
|
||||||
|
bool asleep = false;
|
||||||
|
|
||||||
|
struct ARMCore cpu;
|
||||||
|
|
||||||
|
inline bool isMMU() {
|
||||||
|
return (controlReg & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getRTC();
|
||||||
|
|
||||||
|
uint32_t readReg8(uint32_t reg);
|
||||||
|
uint32_t readReg32(uint32_t reg);
|
||||||
|
void writeReg8(uint32_t reg, uint8_t value);
|
||||||
|
void writeReg32(uint32_t reg, uint32_t value);
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t readPhys8(uint32_t physAddress);
|
||||||
|
uint32_t readPhys16(uint32_t physAddress);
|
||||||
|
uint32_t readPhys32(uint32_t physAddress);
|
||||||
|
void writePhys8(uint32_t physAddress, uint8_t value);
|
||||||
|
void writePhys16(uint32_t physAddress, uint16_t value);
|
||||||
|
void writePhys32(uint32_t physAddress, uint32_t value);
|
||||||
|
|
||||||
|
uint32_t virtToPhys(uint32_t virtAddress);
|
||||||
|
|
||||||
|
uint32_t readVirt8(uint32_t virtAddress) { return readPhys8(virtToPhys(virtAddress)); }
|
||||||
|
uint32_t readVirt16(uint32_t virtAddress) { return readPhys16(virtToPhys(virtAddress)); }
|
||||||
|
uint32_t readVirt32(uint32_t virtAddress) { return readPhys32(virtToPhys(virtAddress)); }
|
||||||
|
void writeVirt8(uint32_t virtAddress, uint8_t value) { writePhys8(virtToPhys(virtAddress), value); }
|
||||||
|
void writeVirt16(uint32_t virtAddress, uint16_t value) { writePhys16(virtToPhys(virtAddress), value); }
|
||||||
|
void writeVirt32(uint32_t virtAddress, uint32_t value) { writePhys32(virtToPhys(virtAddress), value); }
|
||||||
|
|
||||||
|
const uint8_t *getLCDBuffer() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool configured = false;
|
||||||
|
void configure();
|
||||||
|
void configureMemoryBindings();
|
||||||
|
void configureCpuHandlers();
|
||||||
|
|
||||||
|
void printRegs();
|
||||||
|
const char *identifyObjectCon(uint32_t ptr);
|
||||||
|
void fetchStr(uint32_t str, char *buf);
|
||||||
|
void fetchName(uint32_t obj, char *buf);
|
||||||
|
void fetchProcessFilename(uint32_t obj, char *buf);
|
||||||
|
void debugPC(uint32_t pc);
|
||||||
|
|
||||||
|
uint8_t readKeyboard();
|
||||||
|
public:
|
||||||
|
bool keyboardKeys[8*7] = {0};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Emu();
|
||||||
|
void loadROM(const char *path);
|
||||||
|
void dumpRAM(const char *path);
|
||||||
|
void executeUntil(int64_t cycles);
|
||||||
|
int64_t currentCycles() const { return cpu.cycles; }
|
||||||
|
};
|
|
@ -0,0 +1,741 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "isa-arm.h"
|
||||||
|
|
||||||
|
#include "arm.h"
|
||||||
|
#include "emitter-arm.h"
|
||||||
|
#include "isa-inlines.h"
|
||||||
|
|
||||||
|
#define PSR_USER_MASK 0xF0000000
|
||||||
|
#define PSR_PRIV_MASK 0x000000CF
|
||||||
|
#define PSR_STATE_MASK 0x00000020
|
||||||
|
|
||||||
|
// Addressing mode 1
|
||||||
|
static inline void _shiftLSL(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
|
int rm = opcode & 0x0000000F;
|
||||||
|
if (opcode & 0x00000010) {
|
||||||
|
int rs = (opcode >> 8) & 0x0000000F;
|
||||||
|
++cpu->cycles;
|
||||||
|
int shift = cpu->gprs[rs];
|
||||||
|
if (rs == ARM_PC) {
|
||||||
|
shift += 4;
|
||||||
|
}
|
||||||
|
shift &= 0xFF;
|
||||||
|
int32_t shiftVal = cpu->gprs[rm];
|
||||||
|
if (rm == ARM_PC) {
|
||||||
|
shiftVal += 4;
|
||||||
|
}
|
||||||
|
if (!shift) {
|
||||||
|
cpu->shifterOperand = shiftVal;
|
||||||
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
|
} else if (shift < 32) {
|
||||||
|
cpu->shifterOperand = shiftVal << shift;
|
||||||
|
cpu->shifterCarryOut = (shiftVal >> (32 - shift)) & 1;
|
||||||
|
} else if (shift == 32) {
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = shiftVal & 1;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int immediate = (opcode & 0x00000F80) >> 7;
|
||||||
|
if (!immediate) {
|
||||||
|
cpu->shifterOperand = cpu->gprs[rm];
|
||||||
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = cpu->gprs[rm] << immediate;
|
||||||
|
cpu->shifterCarryOut = (cpu->gprs[rm] >> (32 - immediate)) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _shiftLSR(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
|
int rm = opcode & 0x0000000F;
|
||||||
|
if (opcode & 0x00000010) {
|
||||||
|
int rs = (opcode >> 8) & 0x0000000F;
|
||||||
|
++cpu->cycles;
|
||||||
|
int shift = cpu->gprs[rs];
|
||||||
|
if (rs == ARM_PC) {
|
||||||
|
shift += 4;
|
||||||
|
}
|
||||||
|
shift &= 0xFF;
|
||||||
|
uint32_t shiftVal = cpu->gprs[rm];
|
||||||
|
if (rm == ARM_PC) {
|
||||||
|
shiftVal += 4;
|
||||||
|
}
|
||||||
|
if (!shift) {
|
||||||
|
cpu->shifterOperand = shiftVal;
|
||||||
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
|
} else if (shift < 32) {
|
||||||
|
cpu->shifterOperand = shiftVal >> shift;
|
||||||
|
cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1;
|
||||||
|
} else if (shift == 32) {
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = shiftVal >> 31;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int immediate = (opcode & 0x00000F80) >> 7;
|
||||||
|
if (immediate) {
|
||||||
|
cpu->shifterOperand = ((uint32_t) cpu->gprs[rm]) >> immediate;
|
||||||
|
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = ARM_SIGN(cpu->gprs[rm]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _shiftASR(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
|
int rm = opcode & 0x0000000F;
|
||||||
|
if (opcode & 0x00000010) {
|
||||||
|
int rs = (opcode >> 8) & 0x0000000F;
|
||||||
|
++cpu->cycles;
|
||||||
|
int shift = cpu->gprs[rs];
|
||||||
|
if (rs == ARM_PC) {
|
||||||
|
shift += 4;
|
||||||
|
}
|
||||||
|
shift &= 0xFF;
|
||||||
|
int shiftVal = cpu->gprs[rm];
|
||||||
|
if (rm == ARM_PC) {
|
||||||
|
shiftVal += 4;
|
||||||
|
}
|
||||||
|
if (!shift) {
|
||||||
|
cpu->shifterOperand = shiftVal;
|
||||||
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
|
} else if (shift < 32) {
|
||||||
|
cpu->shifterOperand = shiftVal >> shift;
|
||||||
|
cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1;
|
||||||
|
} else if (cpu->gprs[rm] >> 31) {
|
||||||
|
cpu->shifterOperand = 0xFFFFFFFF;
|
||||||
|
cpu->shifterCarryOut = 1;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = 0;
|
||||||
|
cpu->shifterCarryOut = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int immediate = (opcode & 0x00000F80) >> 7;
|
||||||
|
if (immediate) {
|
||||||
|
cpu->shifterOperand = cpu->gprs[rm] >> immediate;
|
||||||
|
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
||||||
|
} else {
|
||||||
|
cpu->shifterCarryOut = ARM_SIGN(cpu->gprs[rm]);
|
||||||
|
cpu->shifterOperand = cpu->shifterCarryOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
|
int rm = opcode & 0x0000000F;
|
||||||
|
if (opcode & 0x00000010) {
|
||||||
|
int rs = (opcode >> 8) & 0x0000000F;
|
||||||
|
++cpu->cycles;
|
||||||
|
int shift = cpu->gprs[rs];
|
||||||
|
if (rs == ARM_PC) {
|
||||||
|
shift += 4;
|
||||||
|
}
|
||||||
|
shift &= 0xFF;
|
||||||
|
int shiftVal = cpu->gprs[rm];
|
||||||
|
if (rm == ARM_PC) {
|
||||||
|
shiftVal += 4;
|
||||||
|
}
|
||||||
|
int rotate = shift & 0x1F;
|
||||||
|
if (!shift) {
|
||||||
|
cpu->shifterOperand = shiftVal;
|
||||||
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
|
} else if (rotate) {
|
||||||
|
cpu->shifterOperand = ROR(shiftVal, rotate);
|
||||||
|
cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = shiftVal;
|
||||||
|
cpu->shifterCarryOut = ARM_SIGN(shiftVal);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int immediate = (opcode & 0x00000F80) >> 7;
|
||||||
|
if (immediate) {
|
||||||
|
cpu->shifterOperand = ROR(cpu->gprs[rm], immediate);
|
||||||
|
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
||||||
|
} else {
|
||||||
|
// RRX
|
||||||
|
cpu->shifterOperand = (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1);
|
||||||
|
cpu->shifterCarryOut = cpu->gprs[rm] & 0x00000001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
|
int immediate = opcode & 0x000000FF;
|
||||||
|
if (!rotate) {
|
||||||
|
cpu->shifterOperand = immediate;
|
||||||
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
|
} else {
|
||||||
|
cpu->shifterOperand = ROR(immediate, rotate);
|
||||||
|
cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instruction definitions
|
||||||
|
// Beware pre-processor antics
|
||||||
|
|
||||||
|
ATTRIBUTE_NOINLINE static void _additionS(struct ARMCore* cpu, int32_t m, int32_t n, int32_t d) {
|
||||||
|
cpu->cpsr.flags = 0;
|
||||||
|
cpu->cpsr.n = ARM_SIGN(d);
|
||||||
|
cpu->cpsr.z = !d;
|
||||||
|
cpu->cpsr.c = ARM_CARRY_FROM(m, n, d);
|
||||||
|
cpu->cpsr.v = ARM_V_ADDITION(m, n, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_NOINLINE static void _subtractionS(struct ARMCore* cpu, int32_t m, int32_t n, int32_t d) {
|
||||||
|
cpu->cpsr.flags = 0;
|
||||||
|
cpu->cpsr.n = ARM_SIGN(d);
|
||||||
|
cpu->cpsr.z = !d;
|
||||||
|
cpu->cpsr.c = ARM_BORROW_FROM(m, n, d);
|
||||||
|
cpu->cpsr.v = ARM_V_SUBTRACTION(m, n, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
|
cpu->cpsr.n = ARM_SIGN(d);
|
||||||
|
cpu->cpsr.z = !d; \
|
||||||
|
cpu->cpsr.c = cpu->shifterCarryOut; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_ADDITION_S(M, N, D) \
|
||||||
|
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
||||||
|
cpu->cpsr = cpu->spsr; \
|
||||||
|
_ARMReadCPSR(cpu); \
|
||||||
|
} else { \
|
||||||
|
_additionS(cpu, M, N, D); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_SUBTRACTION_S(M, N, D) \
|
||||||
|
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
||||||
|
cpu->cpsr = cpu->spsr; \
|
||||||
|
_ARMReadCPSR(cpu); \
|
||||||
|
} else { \
|
||||||
|
_subtractionS(cpu, M, N, D); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_SUBTRACTION_CARRY_S(M, N, D, C) \
|
||||||
|
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
||||||
|
cpu->cpsr = cpu->spsr; \
|
||||||
|
_ARMReadCPSR(cpu); \
|
||||||
|
} else { \
|
||||||
|
cpu->cpsr.n = ARM_SIGN(D); \
|
||||||
|
cpu->cpsr.z = !(D); \
|
||||||
|
cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \
|
||||||
|
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_NEUTRAL_S(M, N, D) \
|
||||||
|
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
||||||
|
cpu->cpsr = cpu->spsr; \
|
||||||
|
_ARMReadCPSR(cpu); \
|
||||||
|
} else { \
|
||||||
|
_neutralS(cpu, D); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_NEUTRAL_HI_S(DLO, DHI) \
|
||||||
|
cpu->cpsr.n = ARM_SIGN(DHI); \
|
||||||
|
cpu->cpsr.z = !((DHI) | (DLO));
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_I_TEST (opcode & 0x00000F80)
|
||||||
|
#define ADDR_MODE_2_I ((opcode & 0x00000F80) >> 7)
|
||||||
|
#define ADDR_MODE_2_ADDRESS (address)
|
||||||
|
#define ADDR_MODE_2_RN (cpu->gprs[rn])
|
||||||
|
#define ADDR_MODE_2_RM (cpu->gprs[rm])
|
||||||
|
#define ADDR_MODE_2_IMMEDIATE (opcode & 0x00000FFF)
|
||||||
|
#define ADDR_MODE_2_INDEX(U_OP, M) (cpu->gprs[rn] U_OP M)
|
||||||
|
#define ADDR_MODE_2_WRITEBACK(ADDR) \
|
||||||
|
cpu->gprs[rn] = ADDR; \
|
||||||
|
if (UNLIKELY(rn == ARM_PC)) { \
|
||||||
|
currentCycles += ARMWritePC(cpu); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_WRITEBACK_PRE_STORE(WB)
|
||||||
|
#define ADDR_MODE_2_WRITEBACK_POST_STORE(WB) WB
|
||||||
|
#define ADDR_MODE_2_WRITEBACK_PRE_LOAD(WB) WB
|
||||||
|
#define ADDR_MODE_2_WRITEBACK_POST_LOAD(WB)
|
||||||
|
|
||||||
|
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
|
||||||
|
#define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0)
|
||||||
|
#define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31)
|
||||||
|
#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1))
|
||||||
|
|
||||||
|
#define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS
|
||||||
|
#define ADDR_MODE_3_RN ADDR_MODE_2_RN
|
||||||
|
#define ADDR_MODE_3_RM ADDR_MODE_2_RM
|
||||||
|
#define ADDR_MODE_3_IMMEDIATE (((opcode & 0x00000F00) >> 4) | (opcode & 0x0000000F))
|
||||||
|
#define ADDR_MODE_3_INDEX(U_OP, M) ADDR_MODE_2_INDEX(U_OP, M)
|
||||||
|
#define ADDR_MODE_3_WRITEBACK(ADDR) ADDR_MODE_2_WRITEBACK(ADDR)
|
||||||
|
|
||||||
|
#define ADDR_MODE_4_WRITEBACK_LDM \
|
||||||
|
if (!((1 << rn) & rs)) { \
|
||||||
|
cpu->gprs[rn] = address; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADDR_MODE_4_WRITEBACK_STM cpu->gprs[rn] = address;
|
||||||
|
|
||||||
|
#define ARM_LOAD_POST_BODY \
|
||||||
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \
|
||||||
|
if (rd == ARM_PC) { \
|
||||||
|
currentCycles += ARMWritePC(cpu); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_STORE_POST_BODY \
|
||||||
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
||||||
|
|
||||||
|
#define DEFINE_INSTRUCTION_ARM(NAME, BODY) \
|
||||||
|
static void _ARMInstruction ## NAME (struct ARMCore* cpu, uint32_t opcode) { \
|
||||||
|
int currentCycles = ARM_PREFETCH_CYCLES; \
|
||||||
|
BODY; \
|
||||||
|
cpu->cycles += currentCycles; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_ALU_INSTRUCTION_EX_ARM(NAME, S_BODY, SHIFTER, BODY) \
|
||||||
|
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||||
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
|
int rn = (opcode >> 16) & 0xF; \
|
||||||
|
UNUSED(rn); \
|
||||||
|
SHIFTER(cpu, opcode); \
|
||||||
|
BODY; \
|
||||||
|
S_BODY; \
|
||||||
|
if (rd == ARM_PC) { \
|
||||||
|
currentCycles += ARMWritePC(cpu); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define DEFINE_ALU_INSTRUCTION_ARM(NAME, S_BODY, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSL, , _shiftLSL, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_LSL, S_BODY, _shiftLSL, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSR, , _shiftLSR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_LSR, S_BODY, _shiftLSR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ASR, , _shiftASR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_ASR, S_BODY, _shiftASR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ROR, , _shiftROR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_ROR, S_BODY, _shiftROR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## I, , _immediate, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## SI, S_BODY, _immediate, BODY)
|
||||||
|
|
||||||
|
#define DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(NAME, S_BODY, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSL, S_BODY, _shiftLSL, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSR, S_BODY, _shiftLSR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ASR, S_BODY, _shiftASR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ROR, S_BODY, _shiftROR, BODY) \
|
||||||
|
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## I, S_BODY, _immediate, BODY)
|
||||||
|
|
||||||
|
#define DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME, BODY, S_BODY) \
|
||||||
|
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||||
|
int rd = (opcode >> 16) & 0xF; \
|
||||||
|
int rs = (opcode >> 8) & 0xF; \
|
||||||
|
int rm = opcode & 0xF; \
|
||||||
|
if (rd == ARM_PC) { \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
ARM_WAIT_MUL(cpu->gprs[rs]); \
|
||||||
|
BODY; \
|
||||||
|
S_BODY; \
|
||||||
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32)
|
||||||
|
|
||||||
|
#define DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME, BODY, S_BODY, WAIT) \
|
||||||
|
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||||
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
|
int rdHi = (opcode >> 16) & 0xF; \
|
||||||
|
int rs = (opcode >> 8) & 0xF; \
|
||||||
|
int rm = opcode & 0xF; \
|
||||||
|
if (rdHi == ARM_PC || rd == ARM_PC) { \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
currentCycles += cpu->memory.stall(cpu, WAIT); \
|
||||||
|
BODY; \
|
||||||
|
S_BODY; \
|
||||||
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32)
|
||||||
|
|
||||||
|
#define DEFINE_MULTIPLY_INSTRUCTION_ARM(NAME, BODY, S_BODY) \
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME, BODY, ) \
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME ## S, BODY, S_BODY)
|
||||||
|
|
||||||
|
#define DEFINE_MULTIPLY_INSTRUCTION_2_ARM(NAME, BODY, S_BODY, WAIT) \
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME, BODY, , WAIT) \
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME ## S, BODY, S_BODY, WAIT)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDRESS, WRITEBACK, LS, BODY) \
|
||||||
|
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||||
|
uint32_t address; \
|
||||||
|
int rn = (opcode >> 16) & 0xF; \
|
||||||
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
|
int rm = opcode & 0xF; \
|
||||||
|
UNUSED(rm); \
|
||||||
|
address = ADDRESS; \
|
||||||
|
ADDR_MODE_2_WRITEBACK_PRE_ ## LS (WRITEBACK); \
|
||||||
|
BODY; \
|
||||||
|
ADDR_MODE_2_WRITEBACK_POST_ ## LS (WRITEBACK);)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME, SHIFTER, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, SHIFTER)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, SHIFTER)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## P, ADDR_MODE_2_INDEX(-, SHIFTER), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PW, ADDR_MODE_2_INDEX(-, SHIFTER), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PU, ADDR_MODE_2_INDEX(+, SHIFTER), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PUW, ADDR_MODE_2_INDEX(+, SHIFTER), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY)
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_INSTRUCTION_ARM(NAME, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _LSL_, ADDR_MODE_2_LSL, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _LSR_, ADDR_MODE_2_LSR, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _ASR_, ADDR_MODE_2_ASR, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IP, ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPW, ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPU, ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPUW, ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(NAME, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## P, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PW, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PU, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PUW, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IP, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPW, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPU, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE), , LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPUW, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME, SHIFTER, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, SHIFTER, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_RM)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, SHIFTER, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_RM)), LS, BODY) \
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(NAME, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _LSL_, ADDR_MODE_2_LSL, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _LSR_, ADDR_MODE_2_LSR, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ASR_, ADDR_MODE_2_ASR, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
||||||
|
|
||||||
|
#define ARM_MS_PRE(IS_LOAD) \
|
||||||
|
enum PrivilegeMode privilegeMode = cpu->privilegeMode; \
|
||||||
|
if (!(opcode & 0x8000) || !IS_LOAD) ARMSetPrivilegeMode(cpu, MODE_USER);
|
||||||
|
|
||||||
|
#define ARM_MS_POST(IS_LOAD) \
|
||||||
|
ARMSetPrivilegeMode(cpu, privilegeMode); \
|
||||||
|
if (IS_LOAD && (opcode & 0x8000)) { \
|
||||||
|
cpu->cpsr.packed = cpu->spsr.packed;\
|
||||||
|
ARMSetPrivilegeMode(cpu, cpu->cpsr.priv);\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME, LS, WRITEBACK, S_PRE, S_POST, DIRECTION, POST_BODY) \
|
||||||
|
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||||
|
int rn = (opcode >> 16) & 0xF; \
|
||||||
|
int rs = opcode & 0x0000FFFF; \
|
||||||
|
uint32_t address = cpu->gprs[rn]; \
|
||||||
|
S_PRE; \
|
||||||
|
address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, ¤tCycles); \
|
||||||
|
POST_BODY; \
|
||||||
|
WRITEBACK; \
|
||||||
|
S_POST; \
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(NAME, LS, IS_LOAD, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DA, LS, , , , DA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , DA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DB, LS, , , , DB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , DB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IA, LS, , , , IA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IB, LS, , , , IB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IA, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IB, POST_BODY) \
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IB, POST_BODY)
|
||||||
|
|
||||||
|
// Begin ALU definitions
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(ADD, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
int32_t n = cpu->gprs[rn];
|
||||||
|
cpu->gprs[rd] = n + cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(ADC, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
int32_t n = cpu->gprs[rn];
|
||||||
|
cpu->gprs[rd] = n + cpu->shifterOperand + cpu->cpsr.c;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(AND, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
cpu->gprs[rd] = cpu->gprs[rn] & cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(BIC, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
cpu->gprs[rd] = cpu->gprs[rn] & ~cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMN, ARM_ADDITION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||||
|
int32_t aluOut = cpu->gprs[rn] + cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMP, ARM_SUBTRACTION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||||
|
int32_t aluOut = cpu->gprs[rn] - cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(EOR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
cpu->gprs[rd] = cpu->gprs[rn] ^ cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(MOV, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
cpu->gprs[rd] = cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(MVN, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
cpu->gprs[rd] = ~cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(ORR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
cpu->gprs[rd] = cpu->gprs[rn] | cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(RSB, ARM_SUBTRACTION_S(cpu->shifterOperand, n, cpu->gprs[rd]),
|
||||||
|
int32_t n = cpu->gprs[rn];
|
||||||
|
cpu->gprs[rd] = cpu->shifterOperand - n;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(RSC, ARM_SUBTRACTION_CARRY_S(cpu->shifterOperand, n, cpu->gprs[rd], !cpu->cpsr.c),
|
||||||
|
int32_t n = cpu->gprs[rn];
|
||||||
|
cpu->gprs[rd] = cpu->shifterOperand - n - !cpu->cpsr.c;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(SBC, ARM_SUBTRACTION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], !cpu->cpsr.c),
|
||||||
|
int32_t n = cpu->gprs[rn];
|
||||||
|
cpu->gprs[rd] = n - cpu->shifterOperand - !cpu->cpsr.c;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_ARM(SUB, ARM_SUBTRACTION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||||
|
int32_t n = cpu->gprs[rn];
|
||||||
|
cpu->gprs[rd] = n - cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TEQ, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||||
|
int32_t aluOut = cpu->gprs[rn] ^ cpu->shifterOperand;)
|
||||||
|
|
||||||
|
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TST, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||||
|
int32_t aluOut = cpu->gprs[rn] & cpu->shifterOperand;)
|
||||||
|
|
||||||
|
// End ALU definitions
|
||||||
|
|
||||||
|
// Begin multiply definitions
|
||||||
|
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(MLA, cpu->gprs[rdHi] = cpu->gprs[rm] * cpu->gprs[rs] + cpu->gprs[rd], ARM_NEUTRAL_S(, , cpu->gprs[rdHi]), 2)
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_ARM(MUL, cpu->gprs[rd] = cpu->gprs[rm] * cpu->gprs[rs], ARM_NEUTRAL_S(cpu->gprs[rm], cpu->gprs[rs], cpu->gprs[rd]))
|
||||||
|
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(SMLAL,
|
||||||
|
int64_t d = ((int64_t) cpu->gprs[rm]) * ((int64_t) cpu->gprs[rs]);
|
||||||
|
int32_t dm = cpu->gprs[rd];
|
||||||
|
int32_t dn = d;
|
||||||
|
cpu->gprs[rd] = dm + dn;
|
||||||
|
cpu->gprs[rdHi] = cpu->gprs[rdHi] + (d >> 32) + ARM_CARRY_FROM(dm, dn, cpu->gprs[rd]);,
|
||||||
|
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 3)
|
||||||
|
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(SMULL,
|
||||||
|
int64_t d = ((int64_t) cpu->gprs[rm]) * ((int64_t) cpu->gprs[rs]);
|
||||||
|
cpu->gprs[rd] = d;
|
||||||
|
cpu->gprs[rdHi] = d >> 32;,
|
||||||
|
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 2)
|
||||||
|
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(UMLAL,
|
||||||
|
uint64_t d = ARM_UXT_64(cpu->gprs[rm]) * ARM_UXT_64(cpu->gprs[rs]);
|
||||||
|
int32_t dm = cpu->gprs[rd];
|
||||||
|
int32_t dn = d;
|
||||||
|
cpu->gprs[rd] = dm + dn;
|
||||||
|
cpu->gprs[rdHi] = cpu->gprs[rdHi] + (d >> 32) + ARM_CARRY_FROM(dm, dn, cpu->gprs[rd]);,
|
||||||
|
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 3)
|
||||||
|
|
||||||
|
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(UMULL,
|
||||||
|
uint64_t d = ARM_UXT_64(cpu->gprs[rm]) * ARM_UXT_64(cpu->gprs[rs]);
|
||||||
|
cpu->gprs[rd] = d;
|
||||||
|
cpu->gprs[rdHi] = d >> 32;,
|
||||||
|
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 2)
|
||||||
|
|
||||||
|
// End multiply definitions
|
||||||
|
|
||||||
|
// Begin load/store definitions
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, LOAD, cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, LOAD, cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, LOAD, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, LOAD, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, LOAD, cpu->gprs[rd] = address & 1 ? ARM_SXT_8(cpu->memory.load16(cpu, address, ¤tCycles)) : ARM_SXT_16(cpu->memory.load16(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, STORE, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, STORE, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||||
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, STORE, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT, LOAD,
|
||||||
|
enum PrivilegeMode priv = cpu->privilegeMode;
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_USER);
|
||||||
|
int32_t r = cpu->memory.load8(cpu, address, ¤tCycles);
|
||||||
|
ARMSetPrivilegeMode(cpu, priv);
|
||||||
|
cpu->gprs[rd] = r;
|
||||||
|
ARM_LOAD_POST_BODY;)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRT, LOAD,
|
||||||
|
enum PrivilegeMode priv = cpu->privilegeMode;
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_USER);
|
||||||
|
int32_t r = cpu->memory.load32(cpu, address, ¤tCycles);
|
||||||
|
ARMSetPrivilegeMode(cpu, priv);
|
||||||
|
cpu->gprs[rd] = r;
|
||||||
|
ARM_LOAD_POST_BODY;)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRBT, STORE,
|
||||||
|
enum PrivilegeMode priv = cpu->privilegeMode;
|
||||||
|
int32_t r = cpu->gprs[rd];
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_USER);
|
||||||
|
cpu->memory.store8(cpu, address, r, ¤tCycles);
|
||||||
|
ARMSetPrivilegeMode(cpu, priv);
|
||||||
|
ARM_STORE_POST_BODY;)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRT, STORE,
|
||||||
|
enum PrivilegeMode priv = cpu->privilegeMode;
|
||||||
|
int32_t r = cpu->gprs[rd];
|
||||||
|
ARMSetPrivilegeMode(cpu, MODE_USER);
|
||||||
|
cpu->memory.store32(cpu, address, r, ¤tCycles);
|
||||||
|
ARMSetPrivilegeMode(cpu, priv);
|
||||||
|
ARM_STORE_POST_BODY;)
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM,
|
||||||
|
load, true,
|
||||||
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
||||||
|
if ((rs & 0x8000) || !rs) {
|
||||||
|
currentCycles += ARMWritePC(cpu);
|
||||||
|
})
|
||||||
|
|
||||||
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(STM,
|
||||||
|
store, false,
|
||||||
|
ARM_STORE_POST_BODY;)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(SWP,
|
||||||
|
int rm = opcode & 0xF;
|
||||||
|
int rd = (opcode >> 12) & 0xF;
|
||||||
|
int rn = (opcode >> 16) & 0xF;
|
||||||
|
int32_t d = cpu->memory.load32(cpu, cpu->gprs[rn], ¤tCycles);
|
||||||
|
cpu->memory.store32(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||||
|
cpu->gprs[rd] = d;)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(SWPB,
|
||||||
|
int rm = opcode & 0xF;
|
||||||
|
int rd = (opcode >> 12) & 0xF;
|
||||||
|
int rn = (opcode >> 16) & 0xF;
|
||||||
|
int32_t d = cpu->memory.load8(cpu, cpu->gprs[rn], ¤tCycles);
|
||||||
|
cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||||
|
cpu->gprs[rd] = d;)
|
||||||
|
|
||||||
|
// End load/store definitions
|
||||||
|
|
||||||
|
// Begin branch definitions
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(B,
|
||||||
|
int32_t offset = opcode << 8;
|
||||||
|
offset >>= 6;
|
||||||
|
cpu->gprs[ARM_PC] += offset;
|
||||||
|
currentCycles += ARMWritePC(cpu);)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(BL,
|
||||||
|
int32_t immediate = (opcode & 0x00FFFFFF) << 8;
|
||||||
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4;
|
||||||
|
cpu->gprs[ARM_PC] += immediate >> 6;
|
||||||
|
currentCycles += ARMWritePC(cpu);)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(BX,
|
||||||
|
int rm = opcode & 0x0000000F;
|
||||||
|
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
|
||||||
|
currentCycles += ARMWritePC(cpu);
|
||||||
|
)
|
||||||
|
|
||||||
|
// End branch definitions
|
||||||
|
|
||||||
|
// Begin coprocessor definitions
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(CDP, ARM_STUB)
|
||||||
|
DEFINE_INSTRUCTION_ARM(LDC, ARM_STUB)
|
||||||
|
DEFINE_INSTRUCTION_ARM(STC, ARM_STUB)
|
||||||
|
DEFINE_INSTRUCTION_ARM(MCR, ARM_STUB)
|
||||||
|
DEFINE_INSTRUCTION_ARM(MRC, ARM_STUB)
|
||||||
|
|
||||||
|
// Begin miscellaneous definitions
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(BKPT, cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF))); // Not strictly in ARMv4T, but here for convenience
|
||||||
|
DEFINE_INSTRUCTION_ARM(ILL, ARM_ILL) // Illegal opcode
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(MSR,
|
||||||
|
int c = opcode & 0x00010000;
|
||||||
|
int f = opcode & 0x00080000;
|
||||||
|
int32_t operand = cpu->gprs[opcode & 0x0000000F];
|
||||||
|
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
||||||
|
if (mask & PSR_USER_MASK) {
|
||||||
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
|
||||||
|
}
|
||||||
|
if (mask & PSR_STATE_MASK) {
|
||||||
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
|
||||||
|
}
|
||||||
|
if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) {
|
||||||
|
ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010));
|
||||||
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
|
||||||
|
}
|
||||||
|
_ARMReadCPSR(cpu);
|
||||||
|
// LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - 4) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
|
// LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
|
cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC] - 4, NULL);
|
||||||
|
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
||||||
|
)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(MSRR,
|
||||||
|
int c = opcode & 0x00010000;
|
||||||
|
int f = opcode & 0x00080000;
|
||||||
|
int32_t operand = cpu->gprs[opcode & 0x0000000F];
|
||||||
|
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
||||||
|
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
|
||||||
|
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(MRS, \
|
||||||
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
|
cpu->gprs[rd] = cpu->cpsr.packed;)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(MRSR, \
|
||||||
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
|
cpu->gprs[rd] = cpu->spsr.packed;)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(MSRI,
|
||||||
|
int c = opcode & 0x00010000;
|
||||||
|
int f = opcode & 0x00080000;
|
||||||
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
|
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
||||||
|
if (mask & PSR_USER_MASK) {
|
||||||
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
|
||||||
|
}
|
||||||
|
if (mask & PSR_STATE_MASK) {
|
||||||
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
|
||||||
|
}
|
||||||
|
if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) {
|
||||||
|
ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010));
|
||||||
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
|
||||||
|
}
|
||||||
|
_ARMReadCPSR(cpu);
|
||||||
|
// LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - 4) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
|
// LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
|
cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC] - 4, NULL);
|
||||||
|
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
||||||
|
)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(MSRRI,
|
||||||
|
int c = opcode & 0x00010000;
|
||||||
|
int f = opcode & 0x00080000;
|
||||||
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
|
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
||||||
|
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
|
||||||
|
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;)
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_ARM(SWI, cpu->irqh.swi32(cpu, opcode & 0xFFFFFF))
|
||||||
|
|
||||||
|
const ARMInstruction _armTable[0x1000] = {
|
||||||
|
DECLARE_ARM_EMITTER_BLOCK(_ARMInstruction)
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef ISA_ARM_H
|
||||||
|
#define ISA_ARM_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define ARM_PREFETCH_CYCLES (1 + cpu->memory.activeSeqCycles32)
|
||||||
|
|
||||||
|
struct ARMCore;
|
||||||
|
|
||||||
|
typedef void (*ARMInstruction)(struct ARMCore*, uint32_t opcode);
|
||||||
|
extern const ARMInstruction _armTable[0x1000];
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef ISA_INLINES_H
|
||||||
|
#define ISA_INLINES_H
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include "arm.h"
|
||||||
|
|
||||||
|
#define ARM_COND_EQ (cpu->cpsr.z)
|
||||||
|
#define ARM_COND_NE (!cpu->cpsr.z)
|
||||||
|
#define ARM_COND_CS (cpu->cpsr.c)
|
||||||
|
#define ARM_COND_CC (!cpu->cpsr.c)
|
||||||
|
#define ARM_COND_MI (cpu->cpsr.n)
|
||||||
|
#define ARM_COND_PL (!cpu->cpsr.n)
|
||||||
|
#define ARM_COND_VS (cpu->cpsr.v)
|
||||||
|
#define ARM_COND_VC (!cpu->cpsr.v)
|
||||||
|
#define ARM_COND_HI (cpu->cpsr.c && !cpu->cpsr.z)
|
||||||
|
#define ARM_COND_LS (!cpu->cpsr.c || cpu->cpsr.z)
|
||||||
|
#define ARM_COND_GE (!cpu->cpsr.n == !cpu->cpsr.v)
|
||||||
|
#define ARM_COND_LT (!cpu->cpsr.n != !cpu->cpsr.v)
|
||||||
|
#define ARM_COND_GT (!cpu->cpsr.z && !cpu->cpsr.n == !cpu->cpsr.v)
|
||||||
|
#define ARM_COND_LE (cpu->cpsr.z || !cpu->cpsr.n != !cpu->cpsr.v)
|
||||||
|
#define ARM_COND_AL 1
|
||||||
|
|
||||||
|
#define ARM_SIGN(I) ((I) >> 31)
|
||||||
|
#define ARM_SXT_8(I) (((int8_t) (I) << 24) >> 24)
|
||||||
|
#define ARM_SXT_16(I) (((int16_t) (I) << 16) >> 16)
|
||||||
|
#define ARM_UXT_64(I) (uint64_t)(uint32_t) (I)
|
||||||
|
|
||||||
|
#define ARM_CARRY_FROM(M, N, D) (((uint32_t) (M) >> 31) + ((uint32_t) (N) >> 31) > ((uint32_t) (D) >> 31))
|
||||||
|
#define ARM_BORROW_FROM(M, N, D) (((uint32_t) (M)) >= ((uint32_t) (N)))
|
||||||
|
#define ARM_BORROW_FROM_CARRY(M, N, D, C) (ARM_UXT_64(M) >= (ARM_UXT_64(N)) + (uint64_t) (C))
|
||||||
|
#define ARM_V_ADDITION(M, N, D) (!(ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
|
||||||
|
#define ARM_V_SUBTRACTION(M, N, D) ((ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
|
||||||
|
|
||||||
|
#define ARM_WAIT_MUL(R) \
|
||||||
|
{ \
|
||||||
|
int32_t wait; \
|
||||||
|
if ((R & 0xFFFFFF00) == 0xFFFFFF00 || !(R & 0xFFFFFF00)) { \
|
||||||
|
wait = 1; \
|
||||||
|
} else if ((R & 0xFFFF0000) == 0xFFFF0000 || !(R & 0xFFFF0000)) { \
|
||||||
|
wait = 2; \
|
||||||
|
} else if ((R & 0xFF000000) == 0xFF000000 || !(R & 0xFF000000)) { \
|
||||||
|
wait = 3; \
|
||||||
|
} else { \
|
||||||
|
wait = 4; \
|
||||||
|
} \
|
||||||
|
currentCycles += cpu->memory.stall(cpu, wait); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_STUB cpu->irqh.hitStub(cpu, opcode)
|
||||||
|
#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode)
|
||||||
|
|
||||||
|
static inline int32_t ARMWritePC(struct ARMCore* cpu) {
|
||||||
|
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -4);
|
||||||
|
// cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
|
||||||
|
// LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
|
cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
||||||
|
cpu->gprs[ARM_PC] += 4;
|
||||||
|
// LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
|
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
||||||
|
return 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
|
||||||
|
return mode != MODE_SYSTEM && mode != MODE_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _ARMReadCPSR(struct ARMCore* cpu) {
|
||||||
|
ARMSetPrivilegeMode(cpu, cpu->cpsr.priv);
|
||||||
|
cpu->irqh.readCPSR(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t _ARMPCAddress(struct ARMCore* cpu) {
|
||||||
|
return cpu->gprs[ARM_PC] - 4 * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef MACROS_H
|
||||||
|
#define MACROS_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define LOAD_64 LOAD_64LE
|
||||||
|
#define LOAD_32 LOAD_32LE
|
||||||
|
#define LOAD_16 LOAD_16LE
|
||||||
|
#define STORE_64 STORE_64LE
|
||||||
|
#define STORE_32 STORE_32LE
|
||||||
|
#define STORE_16 STORE_16LE
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include "wind.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void diffPorts(uint32_t oldval, uint32_t newval) {
|
||||||
|
uint32_t changes = oldval ^ newval;
|
||||||
|
if (changes & 1) printf("PRT codec enable: %d\n", newval&1);
|
||||||
|
if (changes & 2) printf("PRT audio amp enable: %d\n", newval&2);
|
||||||
|
if (changes & 4) printf("PRT lcd power: %d\n", newval&4);
|
||||||
|
if (changes & 8) printf("PRT etna door: %d\n", newval&8);
|
||||||
|
if (changes & 0x10) printf("PRT sled: %d\n", newval&0x10);
|
||||||
|
if (changes & 0x20) printf("PRT pump pwr2: %d\n", newval&0x20);
|
||||||
|
if (changes & 0x40) printf("PRT pump pwr1: %d\n", newval&0x40);
|
||||||
|
if (changes & 0x80) printf("PRT etna err: %d\n", newval&0x80);
|
||||||
|
if (changes & 0x100) printf("PRT rs-232 rts: %d\n", newval&0x100);
|
||||||
|
if (changes & 0x200) printf("PRT rs-232 dtr toggle: %d\n", newval&0x200);
|
||||||
|
if (changes & 0x400) printf("PRT disable power led: %d\n", newval&0x400);
|
||||||
|
if (changes & 0x800) printf("PRT enable uart1: %d\n", newval&0x800);
|
||||||
|
if (changes & 0x1000) printf("PRT lcd backlight: %d\n", newval&0x1000);
|
||||||
|
if (changes & 0x2000) printf("PRT enable uart0: %d\n", newval&0x2000);
|
||||||
|
if (changes & 0x4000) printf("PRT dictaphone: %d\n", newval&0x4000);
|
||||||
|
if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000);
|
||||||
|
if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000);
|
||||||
|
if (changes & 0x40000) printf("PRT contrast0: %d\n", newval&0x40000);
|
||||||
|
if (changes & 0x80000) printf("PRT contrast1: %d\n", newval&0x80000);
|
||||||
|
if (changes & 0x100000) printf("PRT contrast2: %d\n", newval&0x100000);
|
||||||
|
if (changes & 0x200000) printf("PRT contrast3: %d\n", newval&0x200000);
|
||||||
|
if (changes & 0x400000) printf("PRT case open: %d\n", newval&0x400000);
|
||||||
|
if (changes & 0x800000) printf("PRT etna cf power: %d\n", newval&0x800000);
|
||||||
|
if (changes & 0x1000000) printf("PRT kb0: %d\n", newval&0x1000000);
|
||||||
|
if (changes & 0x2000000) printf("PRT kb1: %d\n", newval&0x2000000);
|
||||||
|
if (changes & 0x4000000) printf("PRT kb2: %d\n", newval&0x4000000);
|
||||||
|
if (changes & 0x8000000) printf("PRT kb3: %d\n", newval&0x8000000);
|
||||||
|
if (changes & 0x10000000) printf("PRT kb4: %d\n", newval&0x10000000);
|
||||||
|
if (changes & 0x20000000) printf("PRT kb5: %d\n", newval&0x20000000);
|
||||||
|
if (changes & 0x40000000) printf("PRT kb6: %d\n", newval&0x40000000);
|
||||||
|
if (changes & 0x80000000) printf("PRT kb7: %d\n", newval&0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void diffInterrupts(uint16_t oldval, uint16_t newval) {
|
||||||
|
uint16_t changes = oldval ^ newval;
|
||||||
|
if (changes & 1) printf("INTCHG external=%d\n", newval & 1);
|
||||||
|
if (changes & 2) printf("INTCHG lowbat=%d\n", newval & 2);
|
||||||
|
if (changes & 4) printf("INTCHG watchdog=%d\n", newval & 4);
|
||||||
|
if (changes & 8) printf("INTCHG mediachg=%d\n", newval & 8);
|
||||||
|
if (changes & 0x10) printf("INTCHG codec=%d\n", newval & 0x10);
|
||||||
|
if (changes & 0x20) printf("INTCHG ext1=%d\n", newval & 0x20);
|
||||||
|
if (changes & 0x40) printf("INTCHG ext2=%d\n", newval & 0x40);
|
||||||
|
if (changes & 0x80) printf("INTCHG ext3=%d\n", newval & 0x80);
|
||||||
|
if (changes & 0x100) printf("INTCHG timer1=%d\n", newval & 0x100);
|
||||||
|
if (changes & 0x200) printf("INTCHG timer2=%d\n", newval & 0x200);
|
||||||
|
if (changes & 0x400) printf("INTCHG rtcmatch=%d\n", newval & 0x400);
|
||||||
|
if (changes & 0x800) printf("INTCHG tick=%d\n", newval & 0x800);
|
||||||
|
if (changes & 0x1000) printf("INTCHG uart1=%d\n", newval & 0x1000);
|
||||||
|
if (changes & 0x2000) printf("INTCHG uart2=%d\n", newval & 0x2000);
|
||||||
|
if (changes & 0x4000) printf("INTCHG lcd=%d\n", newval & 0x4000);
|
||||||
|
if (changes & 0x8000) printf("INTCHG spi=%d\n", newval & 0x8000);
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
const int CLOCK_SPEED = 0x9000*1000;
|
||||||
|
const int TICK_INTERVAL = CLOCK_SPEED / 64;
|
||||||
|
|
||||||
|
enum Interrupt {
|
||||||
|
EXTFIQ = 0, // FiqExternal
|
||||||
|
BLINT = 1, // FiqBatLow
|
||||||
|
WEINT = 2, // FiqWatchDog
|
||||||
|
MCINT = 3, // FiqMediaChg
|
||||||
|
CSINT = 4, // IrqCodec
|
||||||
|
EINT1 = 5, // IrqExt1
|
||||||
|
EINT2 = 6, // IrqExt2
|
||||||
|
EINT3 = 7, // IrqExt3
|
||||||
|
TC1OI = 8, // IrqTimer1
|
||||||
|
TC2OI = 9, // IrqTimer2
|
||||||
|
RTCMI = 10, // IrqRtcMatch
|
||||||
|
TINT = 11, // IrqTick
|
||||||
|
UART1 = 12, // IrqUart1
|
||||||
|
UART2 = 13, // IrqUart1
|
||||||
|
LCDINT = 14, // IrqLcd
|
||||||
|
SSEOTI = 15, // IrqSpi
|
||||||
|
FIQ_INTERRUPTS = 0x000F,
|
||||||
|
IRQ_INTERRUPTS = 0xFFF0
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WindermereReg {
|
||||||
|
MEMCFG1 = 0,
|
||||||
|
MEMCFG2 = 4,
|
||||||
|
DRAM_CFG = 0x100,
|
||||||
|
LCDCTL = 0x200,
|
||||||
|
LCDST = 0x204,
|
||||||
|
LCD_DBAR1 = 0x210,
|
||||||
|
LCDT0 = 0x220,
|
||||||
|
LCDT1 = 0x224,
|
||||||
|
LCDT2 = 0x228,
|
||||||
|
PWRSR = 0x400,
|
||||||
|
PWRCNT = 0x404,
|
||||||
|
HALT = 0x408,
|
||||||
|
STBY = 0x40C,
|
||||||
|
BLEOI = 0x410,
|
||||||
|
MCEOI = 0x414,
|
||||||
|
TEOI = 0x418,
|
||||||
|
STFCLR = 0x41C,
|
||||||
|
E2EOI = 0x420,
|
||||||
|
INTSR = 0x500,
|
||||||
|
INTRSR = 0x504,
|
||||||
|
INTENS = 0x508,
|
||||||
|
INTENC = 0x50C,
|
||||||
|
INTTEST1 = 0x514,
|
||||||
|
INTTEST2 = 0x518,
|
||||||
|
UART0DATA = 0x600,
|
||||||
|
UART0FCR = 0x604,
|
||||||
|
UART0LCR = 0x608,
|
||||||
|
UART0CON = 0x60C,
|
||||||
|
UART0FLG = 0x610,
|
||||||
|
UART0INT = 0x614,
|
||||||
|
UART0INTM = 0x618,
|
||||||
|
UART0INTR = 0x61C,
|
||||||
|
UART0TEST1 = 0x620,
|
||||||
|
UART0TEST2 = 0x624,
|
||||||
|
UART0TEST3 = 0x628,
|
||||||
|
UART1DATA = 0x700,
|
||||||
|
UART1FCR = 0x704,
|
||||||
|
UART1LCR = 0x708,
|
||||||
|
UART1CON = 0x70C,
|
||||||
|
UART1FLG = 0x710,
|
||||||
|
UART1INT = 0x714,
|
||||||
|
UART1INTM = 0x718,
|
||||||
|
UART1INTR = 0x71C,
|
||||||
|
UART1TEST1 = 0x720,
|
||||||
|
UART1TEST2 = 0x724,
|
||||||
|
UART1TEST3 = 0x728,
|
||||||
|
PUMPCON = 0x900,
|
||||||
|
CODR = 0xA00,
|
||||||
|
CONFG = 0xA04,
|
||||||
|
COLFG = 0xA08,
|
||||||
|
COEOI = 0xA0C,
|
||||||
|
COTEST = 0xA10,
|
||||||
|
SSCR0 = 0xB00,
|
||||||
|
SSCR1 = 0xB04,
|
||||||
|
SSDR = 0xB0C,
|
||||||
|
SSSR = 0xB14,
|
||||||
|
TC1LOAD = 0xC00,
|
||||||
|
TC1VAL = 0xC04,
|
||||||
|
TC1CTRL = 0xC08,
|
||||||
|
TC1EOI = 0xC0C,
|
||||||
|
TC2LOAD = 0xC20,
|
||||||
|
TC2VAL = 0xC24,
|
||||||
|
TC2CTRL = 0xC28,
|
||||||
|
TC2EOI = 0xC2C,
|
||||||
|
BZCONT = 0xC40,
|
||||||
|
RTCDRL = 0xD00,
|
||||||
|
RTCDRU = 0xD04,
|
||||||
|
RTCMRL = 0xD08,
|
||||||
|
RTCMRU = 0xD0C,
|
||||||
|
RTCEOI = 0xD10,
|
||||||
|
PADR = 0xE00,
|
||||||
|
PBDR = 0xE04,
|
||||||
|
PCDR = 0xE08,
|
||||||
|
PDDR = 0xE0C,
|
||||||
|
PADDR = 0xE10,
|
||||||
|
PBDDR = 0xE14,
|
||||||
|
PCDDR = 0xE18,
|
||||||
|
PDDDR = 0xE1C,
|
||||||
|
PEDR = 0xE20,
|
||||||
|
PEDDR = 0xE24,
|
||||||
|
KSCAN = 0xE28,
|
||||||
|
LCDMUX = 0xE2C
|
||||||
|
};
|
||||||
|
|
||||||
|
void diffPorts(uint32_t oldval, uint32_t newval);
|
||||||
|
void diffInterrupts(uint16_t oldval, uint16_t newval);
|
|
@ -0,0 +1,153 @@
|
||||||
|
#pragma once
|
||||||
|
#include "wind.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct Timer {
|
||||||
|
struct ARMCore *cpu;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TICK_INTERVAL_SLOW = CLOCK_SPEED / 2000,
|
||||||
|
TICK_INTERVAL_FAST = CLOCK_SPEED / 512000,
|
||||||
|
MODE_512KHZ = 1<<3,
|
||||||
|
PERIODIC = 1<<6,
|
||||||
|
ENABLED = 1<<7
|
||||||
|
};
|
||||||
|
int64_t lastTicked;
|
||||||
|
uint8_t config;
|
||||||
|
uint32_t interval;
|
||||||
|
int32_t value;
|
||||||
|
|
||||||
|
int tickInterval() const {
|
||||||
|
return (config & MODE_512KHZ) ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW;
|
||||||
|
}
|
||||||
|
void load(uint32_t lval) {
|
||||||
|
interval = lval;
|
||||||
|
value = lval;
|
||||||
|
}
|
||||||
|
bool tick(int64_t cycles) {
|
||||||
|
if (cycles >= (lastTicked + tickInterval())) {
|
||||||
|
lastTicked += tickInterval();
|
||||||
|
|
||||||
|
if (config & ENABLED) {
|
||||||
|
--value;
|
||||||
|
if (value == 0) {
|
||||||
|
if (config & PERIODIC)
|
||||||
|
value = interval;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void dump() {
|
||||||
|
printf("enabled=%s periodic=%s interval=%d value=%d\n",
|
||||||
|
(config & ENABLED) ? "true" : "false",
|
||||||
|
(config & PERIODIC) ? "true" : "false",
|
||||||
|
interval, value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UART {
|
||||||
|
struct ARMCore *cpu;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IntRx = 1,
|
||||||
|
IntTx = 2,
|
||||||
|
IntModemStatus = 4,
|
||||||
|
PortCtrlEnable = 1,
|
||||||
|
PortCtrlSirEnable = 2,
|
||||||
|
PortCtrlIrdaTx = 4,
|
||||||
|
FrameCtrlBreak = 1,
|
||||||
|
FrameCtrlParityEnable = 2,
|
||||||
|
FrameCtrlEvenParity = 4,
|
||||||
|
FrameCtrlExtraStopBit = 8,
|
||||||
|
FrameCtrlUFifoEn = 0x10,
|
||||||
|
FrameCtrlWrdLenMask = 0x60,
|
||||||
|
FrameCtrlWlen5 = 0,
|
||||||
|
FrameCtrlWlen6 = 0x20,
|
||||||
|
FrameCtrlWlen7 = 0x40,
|
||||||
|
FrameCtrlWlen8 = 0x60,
|
||||||
|
RecvFrameError = 0x100,
|
||||||
|
RecvParityError = 0x200,
|
||||||
|
RecvOverrunError = 0x400,
|
||||||
|
FlagClearToSend = 1,
|
||||||
|
FlagDataSetReady = 2,
|
||||||
|
FlagDataCarrierDetect = 4,
|
||||||
|
FlagBusy = 8,
|
||||||
|
FlagReceiveFifoEmpty = 0x10,
|
||||||
|
FlagTransmitFifoFull = 0x20
|
||||||
|
};
|
||||||
|
uint8_t portControl = 0;
|
||||||
|
uint8_t frameControl = 0;
|
||||||
|
uint8_t interrupts = 0, interruptMask = 0;
|
||||||
|
|
||||||
|
// UART0DATA = 0x600, byte write, long read
|
||||||
|
// UART0FCR = 0x604, long
|
||||||
|
// UART0LCR = 0x608, long
|
||||||
|
// UART0CON = 0x60C, byte
|
||||||
|
// UART0FLG = 0x610, byte
|
||||||
|
// UART0INT = 0x614, long write, byte read
|
||||||
|
// UART0INTM = 0x618, byte
|
||||||
|
// UART0INTR = 0x61C, byte
|
||||||
|
// UART0TEST1 = 0x620,
|
||||||
|
// UART0TEST2 = 0x624,
|
||||||
|
// UART0TEST3 = 0x628,
|
||||||
|
uint32_t readReg8(uint32_t reg) {
|
||||||
|
// UART0DATA
|
||||||
|
if (reg == (UART0CON & 0xFF)) {
|
||||||
|
return portControl;
|
||||||
|
} else if (reg == (UART0FLG & 0xFF)) {
|
||||||
|
// we pretend we are never busy, never have full fifo
|
||||||
|
return FlagReceiveFifoEmpty;
|
||||||
|
// UART0INT?
|
||||||
|
// UART0INTM?
|
||||||
|
// UART0INTR?
|
||||||
|
} else {
|
||||||
|
printf("unhandled 8bit uart read %x at pc=%08x lr=%08x\n", reg, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t readReg32(uint32_t reg) {
|
||||||
|
// UART0DATA
|
||||||
|
if (reg == (UART0FCR & 0xFF)) {
|
||||||
|
return frameControl;
|
||||||
|
// UART0LCR
|
||||||
|
} else {
|
||||||
|
printf("unhandled 32bit uart read %x at pc=%08x lr=%08x\n", reg, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void writeReg8(uint32_t reg, uint8_t value) {
|
||||||
|
// UART0DATA
|
||||||
|
if (reg == (UART0CON & 0xFF)) {
|
||||||
|
portControl = value;
|
||||||
|
printf("portcon updated: enable=%d sirenable=%d irdatx=%d\n", value&1, value&2, value&4);
|
||||||
|
} else if (reg == (UART0INTM & 0xFF)) {
|
||||||
|
interruptMask = value;
|
||||||
|
printf("uart interruptmask updated: %d\n", value);
|
||||||
|
// UART0INTR?
|
||||||
|
} else {
|
||||||
|
printf("unhandled 8bit uart write %x value %02x at pc=%08x lr=%08x\n", reg, value, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void writeReg32(uint32_t reg, uint32_t value) {
|
||||||
|
if (reg == (UART0FCR & 0xFF)) {
|
||||||
|
frameControl = value;
|
||||||
|
printf("frameControl updated: break=%d parityEn=%d evenParity=%d extraStop=%d ufifoEn=%d wrdLen=%d\n",
|
||||||
|
value&1,
|
||||||
|
value&2,
|
||||||
|
value&4,
|
||||||
|
value&8,
|
||||||
|
value&0x10,
|
||||||
|
((value&0x60)>>5)+5);
|
||||||
|
} else if (reg == (UART0LCR & 0xFF)) {
|
||||||
|
printf("** uart writing lcr %x **\n", value);
|
||||||
|
} else if (reg == (UART0INT & 0xFF)) {
|
||||||
|
printf("uart interrupts %x -> %x\n", interrupts, value);
|
||||||
|
interrupts = value;
|
||||||
|
} else {
|
||||||
|
printf("unhandled 32bit uart write %x value %08x at pc=%08x lr=%08x\n", reg, value, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS += \
|
||||||
|
WindQt \
|
||||||
|
WindCore
|
|
@ -0,0 +1,51 @@
|
||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2019-12-18T16:17:36
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += core gui widgets
|
||||||
|
|
||||||
|
TARGET = WindQt
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
CONFIG += c++11
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../WindCore/release/ -lWindCore
|
||||||
|
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../WindCore/debug/ -lWindCore
|
||||||
|
else:unix: LIBS += -L$$OUT_PWD/../WindCore/ -lWindCore
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD/../WindCore
|
||||||
|
DEPENDPATH += $$PWD/../WindCore
|
||||||
|
|
||||||
|
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../WindCore/release/libWindCore.a
|
||||||
|
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../WindCore/debug/libWindCore.a
|
||||||
|
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../WindCore/release/WindCore.lib
|
||||||
|
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../WindCore/debug/WindCore.lib
|
||||||
|
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../WindCore/libWindCore.a
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include "../WindCore/wind.h"
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
emu = new Emu;
|
||||||
|
emu->loadROM("/Users/ash/src/psion/Sys$rom.bin");
|
||||||
|
|
||||||
|
timer = new QTimer(this);
|
||||||
|
timer->start(1000/64);
|
||||||
|
connect(timer, SIGNAL(timeout()), SLOT(execTimer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::execTimer()
|
||||||
|
{
|
||||||
|
emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED / 64));
|
||||||
|
ui->cycleCounter->setText(QString("Cycles: %1").arg(emu->currentCycles()));
|
||||||
|
updateScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_stepButton_clicked()
|
||||||
|
{
|
||||||
|
emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED * 2));
|
||||||
|
ui->cycleCounter->setText(QString("Cycles: %1").arg(emu->currentCycles()));
|
||||||
|
updateScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateScreen()
|
||||||
|
{
|
||||||
|
const uint8_t *lcdBuf = emu->getLCDBuffer();
|
||||||
|
if (lcdBuf) {
|
||||||
|
QImage img(640, 240, QImage::Format_Grayscale8);
|
||||||
|
|
||||||
|
// fetch palette
|
||||||
|
int bpp = 1 << (lcdBuf[1] >> 4);
|
||||||
|
int ppb = 8 / bpp;
|
||||||
|
uint16_t palette[16];
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
palette[i] = lcdBuf[i*2] | ((lcdBuf[i*2+1] << 8) & 0xF00);
|
||||||
|
|
||||||
|
// build our image out
|
||||||
|
int lineWidth = (img.width() * bpp) / 8;
|
||||||
|
for (int y = 0; y < img.height(); y++) {
|
||||||
|
uint8_t *scanline = img.scanLine(y);
|
||||||
|
int lineOffs = 0x20 + (lineWidth * y);
|
||||||
|
for (int x = 0; x < img.width(); x++) {
|
||||||
|
uint8_t byte = lcdBuf[lineOffs + (x / ppb)];
|
||||||
|
int shift = (x & (ppb - 1)) * bpp;
|
||||||
|
int mask = (bpp << 1) - 1;
|
||||||
|
int palIdx = (byte >> shift) & mask;
|
||||||
|
int palValue = palette[palIdx];
|
||||||
|
|
||||||
|
if (bpp <= 1)
|
||||||
|
palValue |= (palValue << 1);
|
||||||
|
if (bpp <= 2)
|
||||||
|
palValue |= (palValue << 2);
|
||||||
|
if (bpp <= 4)
|
||||||
|
palValue |= (palValue << 4);
|
||||||
|
scanline[x] = palValue ^ 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->screen->setPixmap(QPixmap::fromImage(std::move(img)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int resolveKey(int key) {
|
||||||
|
switch (key) {
|
||||||
|
case Qt::Key_6: return 0;
|
||||||
|
case Qt::Key_5: return 1;
|
||||||
|
case Qt::Key_4: return 2;
|
||||||
|
case Qt::Key_3: return 3;
|
||||||
|
case Qt::Key_2: return 4;
|
||||||
|
case Qt::Key_1: return 5;
|
||||||
|
// missing 6: F13/rec
|
||||||
|
|
||||||
|
case Qt::Key_Apostrophe: return 7;
|
||||||
|
case Qt::Key_Backspace: return 8;
|
||||||
|
case Qt::Key_0: return 9;
|
||||||
|
case Qt::Key_9: return 10;
|
||||||
|
case Qt::Key_8: return 11;
|
||||||
|
case Qt::Key_7: return 12;
|
||||||
|
// missing 13: F15/play
|
||||||
|
|
||||||
|
case Qt::Key_Y: return 14;
|
||||||
|
case Qt::Key_T: return 15;
|
||||||
|
case Qt::Key_R: return 16;
|
||||||
|
case Qt::Key_E: return 17;
|
||||||
|
case Qt::Key_W: return 18;
|
||||||
|
case Qt::Key_Q: return 19;
|
||||||
|
case Qt::Key_Escape: return 20;
|
||||||
|
|
||||||
|
case Qt::Key_Enter: return 21;
|
||||||
|
case Qt::Key_Return: return 21;
|
||||||
|
case Qt::Key_L: return 22;
|
||||||
|
case Qt::Key_P: return 23;
|
||||||
|
case Qt::Key_O: return 24;
|
||||||
|
case Qt::Key_I: return 25;
|
||||||
|
case Qt::Key_U: return 26;
|
||||||
|
case Qt::Key_Alt: return 27; // actually Menu
|
||||||
|
|
||||||
|
case Qt::Key_G: return 28;
|
||||||
|
case Qt::Key_F: return 29;
|
||||||
|
case Qt::Key_D: return 30;
|
||||||
|
case Qt::Key_S: return 31;
|
||||||
|
case Qt::Key_A: return 32;
|
||||||
|
case Qt::Key_Tab: return 33;
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
case Qt::Key_Meta: return 34; // Control -> Control
|
||||||
|
#else
|
||||||
|
case Qt::Key_Control: return 34; // Control -> Control
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case Qt::Key_Down: return 35;
|
||||||
|
case Qt::Key_Period: return 36;
|
||||||
|
case Qt::Key_M: return 37;
|
||||||
|
case Qt::Key_K: return 38;
|
||||||
|
case Qt::Key_J: return 39;
|
||||||
|
case Qt::Key_H: return 40;
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
case Qt::Key_Control: return 41; // Command -> Fn
|
||||||
|
#else
|
||||||
|
case Qt::Key_Meta: return 41; // Super -> Fn
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case Qt::Key_N: return 42;
|
||||||
|
case Qt::Key_B: return 43;
|
||||||
|
case Qt::Key_V: return 44;
|
||||||
|
case Qt::Key_C: return 45;
|
||||||
|
case Qt::Key_X: return 46;
|
||||||
|
case Qt::Key_Z: return 47;
|
||||||
|
case Qt::Key_Shift: return 48;
|
||||||
|
|
||||||
|
case Qt::Key_Right: return 49;
|
||||||
|
case Qt::Key_Left: return 50;
|
||||||
|
case Qt::Key_Comma: return 51;
|
||||||
|
case Qt::Key_Up: return 52;
|
||||||
|
case Qt::Key_Space: return 53;
|
||||||
|
// missing 54: F14/stop
|
||||||
|
// missing 55: another Shift
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
int k = resolveKey(event->key());
|
||||||
|
if (k >= 0)
|
||||||
|
emu->keyboardKeys[k] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::keyReleaseEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
int k = resolveKey(event->key());
|
||||||
|
if (k >= 0)
|
||||||
|
emu->keyboardKeys[k] = false;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include "../WindCore/emu.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_stepButton_clicked();
|
||||||
|
void execTimer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
Emu *emu;
|
||||||
|
QTimer *timer;
|
||||||
|
void updateScreen();
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>752</width>
|
||||||
|
<height>429</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="2">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="3">
|
||||||
|
<widget class="QPushButton" name="stepButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Step</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="cycleCounter">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cycles</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="4">
|
||||||
|
<widget class="QLabel" name="screen">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
Reference in New Issue