initial commit

This commit is contained in:
Ash Wolf 2019-12-19 00:27:23 +00:00
commit 1d6e77ced8
28 changed files with 4910 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pro.user*

373
LICENSE Normal file
View File

@ -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.

53
README.md Normal file
View File

@ -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.

48
WindCore/WindCore.pro Normal file
View File

@ -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
}

236
WindCore/arm.c Normal file
View File

@ -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;
}

174
WindCore/arm.h Normal file
View File

@ -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

280
WindCore/common.h Normal file
View File

@ -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

447
WindCore/decoder-arm.c Normal file
View File

@ -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);
}

View File

@ -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

479
WindCore/decoder.c Normal file
View File

@ -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;
}

219
WindCore/decoder.h Normal file
View File

@ -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

335
WindCore/emitter-arm.h Normal file
View File

@ -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

View File

@ -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

609
WindCore/emu.cpp Normal file
View File

@ -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;
}

85
WindCore/emu.h Normal file
View File

@ -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; }
};

741
WindCore/isa-arm.c Normal file
View File

@ -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, &currentCycles); \
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, &currentCycles); ARM_LOAD_POST_BODY;)
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, LOAD, cpu->gprs[rd] = cpu->memory.load8(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;)
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, LOAD, cpu->gprs[rd] = cpu->memory.load16(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;)
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, LOAD, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, &currentCycles)); 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, &currentCycles)) : ARM_SXT_16(cpu->memory.load16(cpu, address, &currentCycles)); ARM_LOAD_POST_BODY;)
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, STORE, cpu->memory.store32(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;)
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, STORE, cpu->memory.store8(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;)
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, STORE, cpu->memory.store16(cpu, address, cpu->gprs[rd], &currentCycles); 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, &currentCycles);
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, &currentCycles);
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, &currentCycles);
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, &currentCycles);
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], &currentCycles);
cpu->memory.store32(cpu, cpu->gprs[rn], cpu->gprs[rm], &currentCycles);
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], &currentCycles);
cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], &currentCycles);
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)
};

18
WindCore/isa-arm.h Normal file
View File

@ -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

82
WindCore/isa-inlines.h Normal file
View File

@ -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

18
WindCore/macros.h Normal file
View File

@ -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

57
WindCore/wind.cpp Normal file
View File

@ -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);
}

114
WindCore/wind.h Normal file
View File

@ -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);

153
WindCore/wind_hw.h Normal file
View File

@ -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]);
}
}
};

5
WindEmu.pro Normal file
View File

@ -0,0 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += \
WindQt \
WindCore

51
WindQt/WindQt.pro Normal file
View File

@ -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

11
WindQt/main.cpp Normal file
View File

@ -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();
}

171
WindQt/mainwindow.cpp Normal file
View File

@ -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;
}

35
WindQt/mainwindow.h Normal file
View File

@ -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

58
WindQt/mainwindow.ui Normal file
View File

@ -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>