From 1d6e77ced8a1bc6bef40f4dc5fb15e7931aa2a1b Mon Sep 17 00:00:00 2001 From: Ash Wolf Date: Thu, 19 Dec 2019 00:27:23 +0000 Subject: [PATCH] initial commit --- .gitignore | 1 + LICENSE | 373 +++++++++++++++++++ README.md | 53 +++ WindCore/WindCore.pro | 48 +++ WindCore/arm.c | 236 ++++++++++++ WindCore/arm.h | 174 +++++++++ WindCore/common.h | 280 ++++++++++++++ WindCore/decoder-arm.c | 447 ++++++++++++++++++++++ WindCore/decoder-inlines.h | 25 ++ WindCore/decoder.c | 479 ++++++++++++++++++++++++ WindCore/decoder.h | 219 +++++++++++ WindCore/emitter-arm.h | 335 +++++++++++++++++ WindCore/emitter-inlines.h | 32 ++ WindCore/emu.cpp | 609 ++++++++++++++++++++++++++++++ WindCore/emu.h | 85 +++++ WindCore/isa-arm.c | 741 +++++++++++++++++++++++++++++++++++++ WindCore/isa-arm.h | 18 + WindCore/isa-inlines.h | 82 ++++ WindCore/macros.h | 18 + WindCore/wind.cpp | 57 +++ WindCore/wind.h | 114 ++++++ WindCore/wind_hw.h | 153 ++++++++ WindEmu.pro | 5 + WindQt/WindQt.pro | 51 +++ WindQt/main.cpp | 11 + WindQt/mainwindow.cpp | 171 +++++++++ WindQt/mainwindow.h | 35 ++ WindQt/mainwindow.ui | 58 +++ 28 files changed, 4910 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 WindCore/WindCore.pro create mode 100644 WindCore/arm.c create mode 100644 WindCore/arm.h create mode 100644 WindCore/common.h create mode 100644 WindCore/decoder-arm.c create mode 100644 WindCore/decoder-inlines.h create mode 100644 WindCore/decoder.c create mode 100644 WindCore/decoder.h create mode 100644 WindCore/emitter-arm.h create mode 100644 WindCore/emitter-inlines.h create mode 100644 WindCore/emu.cpp create mode 100644 WindCore/emu.h create mode 100644 WindCore/isa-arm.c create mode 100644 WindCore/isa-arm.h create mode 100644 WindCore/isa-inlines.h create mode 100644 WindCore/macros.h create mode 100644 WindCore/wind.cpp create mode 100644 WindCore/wind.h create mode 100644 WindCore/wind_hw.h create mode 100644 WindEmu.pro create mode 100644 WindQt/WindQt.pro create mode 100644 WindQt/main.cpp create mode 100644 WindQt/mainwindow.cpp create mode 100644 WindQt/mainwindow.h create mode 100644 WindQt/mainwindow.ui diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6df9758 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pro.user* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae19f90 --- /dev/null +++ b/README.md @@ -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. + + + + diff --git a/WindCore/WindCore.pro b/WindCore/WindCore.pro new file mode 100644 index 0000000..23b825a --- /dev/null +++ b/WindCore/WindCore.pro @@ -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 +} diff --git a/WindCore/arm.c b/WindCore/arm.c new file mode 100644 index 0000000..80c7f0e --- /dev/null +++ b/WindCore/arm.c @@ -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; +} diff --git a/WindCore/arm.h b/WindCore/arm.h new file mode 100644 index 0000000..0e61e6c --- /dev/null +++ b/WindCore/arm.h @@ -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 + +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 diff --git a/WindCore/common.h b/WindCore/common.h new file mode 100644 index 0000000..0b0e6d9 --- /dev/null +++ b/WindCore/common.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +// WinSock2 gets very angry if it's included too late +#include +#endif + +#if defined(_MSC_VER) || defined(__cplusplus) +#define restrict __restrict +#endif + +#ifdef _MSC_VER +#include +#include +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 +typedef intptr_t ssize_t; +#else +#include +#include +#include +#endif + +#ifdef PSP2 +// For PATH_MAX on modern toolchains +#include +#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 diff --git a/WindCore/decoder-arm.c b/WindCore/decoder-arm.c new file mode 100644 index 0000000..afbb03d --- /dev/null +++ b/WindCore/decoder-arm.c @@ -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); +} diff --git a/WindCore/decoder-inlines.h b/WindCore/decoder-inlines.h new file mode 100644 index 0000000..ad10838 --- /dev/null +++ b/WindCore/decoder-inlines.h @@ -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 +#include + +#define LOAD_CYCLES \ + info->iCycles = 1; \ + info->nDataCycles = 1; + +#define STORE_CYCLES \ + info->sInstructionCycles = 0; \ + info->nInstructionCycles = 1; \ + info->nDataCycles = 1; + +#endif diff --git a/WindCore/decoder.c b/WindCore/decoder.c new file mode 100644 index 0000000..9e8545c --- /dev/null +++ b/WindCore/decoder.c @@ -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; +} diff --git a/WindCore/decoder.h b/WindCore/decoder.h new file mode 100644 index 0000000..79007b5 --- /dev/null +++ b/WindCore/decoder.h @@ -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 diff --git a/WindCore/emitter-arm.h b/WindCore/emitter-arm.h new file mode 100644 index 0000000..e99a1b7 --- /dev/null +++ b/WindCore/emitter-arm.h @@ -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 diff --git a/WindCore/emitter-inlines.h b/WindCore/emitter-inlines.h new file mode 100644 index 0000000..38bab70 --- /dev/null +++ b/WindCore/emitter-inlines.h @@ -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 diff --git a/WindCore/emu.cpp b/WindCore/emu.cpp new file mode 100644 index 0000000..b8fa8de --- /dev/null +++ b/WindCore/emu.cpp @@ -0,0 +1,609 @@ +#include "emu.h" +#include "wind.h" +#include "wind_hw.h" +#include + + + +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<= 30000000) { +// static bool lcdtest = false; +// if (!lcdtest) { +// printf("lcdtest\n"); +// pendingInterrupts |= (1<"); + 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; +} diff --git a/WindCore/emu.h b/WindCore/emu.h new file mode 100644 index 0000000..e743987 --- /dev/null +++ b/WindCore/emu.h @@ -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; } +}; diff --git a/WindCore/isa-arm.c b/WindCore/isa-arm.c new file mode 100644 index 0000000..28970bc --- /dev/null +++ b/WindCore/isa-arm.c @@ -0,0 +1,741 @@ +/* Copyright (c) 2013-2014 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "isa-arm.h" + +#include "arm.h" +#include "emitter-arm.h" +#include "isa-inlines.h" + +#define PSR_USER_MASK 0xF0000000 +#define PSR_PRIV_MASK 0x000000CF +#define PSR_STATE_MASK 0x00000020 + +// Addressing mode 1 +static inline void _shiftLSL(struct ARMCore* cpu, uint32_t opcode) { + int rm = opcode & 0x0000000F; + if (opcode & 0x00000010) { + int rs = (opcode >> 8) & 0x0000000F; + ++cpu->cycles; + int shift = cpu->gprs[rs]; + if (rs == ARM_PC) { + shift += 4; + } + shift &= 0xFF; + int32_t shiftVal = cpu->gprs[rm]; + if (rm == ARM_PC) { + shiftVal += 4; + } + if (!shift) { + cpu->shifterOperand = shiftVal; + cpu->shifterCarryOut = cpu->cpsr.c; + } else if (shift < 32) { + cpu->shifterOperand = shiftVal << shift; + cpu->shifterCarryOut = (shiftVal >> (32 - shift)) & 1; + } else if (shift == 32) { + cpu->shifterOperand = 0; + cpu->shifterCarryOut = shiftVal & 1; + } else { + cpu->shifterOperand = 0; + cpu->shifterCarryOut = 0; + } + } else { + int immediate = (opcode & 0x00000F80) >> 7; + if (!immediate) { + cpu->shifterOperand = cpu->gprs[rm]; + cpu->shifterCarryOut = cpu->cpsr.c; + } else { + cpu->shifterOperand = cpu->gprs[rm] << immediate; + cpu->shifterCarryOut = (cpu->gprs[rm] >> (32 - immediate)) & 1; + } + } +} + +static inline void _shiftLSR(struct ARMCore* cpu, uint32_t opcode) { + int rm = opcode & 0x0000000F; + if (opcode & 0x00000010) { + int rs = (opcode >> 8) & 0x0000000F; + ++cpu->cycles; + int shift = cpu->gprs[rs]; + if (rs == ARM_PC) { + shift += 4; + } + shift &= 0xFF; + uint32_t shiftVal = cpu->gprs[rm]; + if (rm == ARM_PC) { + shiftVal += 4; + } + if (!shift) { + cpu->shifterOperand = shiftVal; + cpu->shifterCarryOut = cpu->cpsr.c; + } else if (shift < 32) { + cpu->shifterOperand = shiftVal >> shift; + cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1; + } else if (shift == 32) { + cpu->shifterOperand = 0; + cpu->shifterCarryOut = shiftVal >> 31; + } else { + cpu->shifterOperand = 0; + cpu->shifterCarryOut = 0; + } + } else { + int immediate = (opcode & 0x00000F80) >> 7; + if (immediate) { + cpu->shifterOperand = ((uint32_t) cpu->gprs[rm]) >> immediate; + cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1; + } else { + cpu->shifterOperand = 0; + cpu->shifterCarryOut = ARM_SIGN(cpu->gprs[rm]); + } + } +} + +static inline void _shiftASR(struct ARMCore* cpu, uint32_t opcode) { + int rm = opcode & 0x0000000F; + if (opcode & 0x00000010) { + int rs = (opcode >> 8) & 0x0000000F; + ++cpu->cycles; + int shift = cpu->gprs[rs]; + if (rs == ARM_PC) { + shift += 4; + } + shift &= 0xFF; + int shiftVal = cpu->gprs[rm]; + if (rm == ARM_PC) { + shiftVal += 4; + } + if (!shift) { + cpu->shifterOperand = shiftVal; + cpu->shifterCarryOut = cpu->cpsr.c; + } else if (shift < 32) { + cpu->shifterOperand = shiftVal >> shift; + cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1; + } else if (cpu->gprs[rm] >> 31) { + cpu->shifterOperand = 0xFFFFFFFF; + cpu->shifterCarryOut = 1; + } else { + cpu->shifterOperand = 0; + cpu->shifterCarryOut = 0; + } + } else { + int immediate = (opcode & 0x00000F80) >> 7; + if (immediate) { + cpu->shifterOperand = cpu->gprs[rm] >> immediate; + cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1; + } else { + cpu->shifterCarryOut = ARM_SIGN(cpu->gprs[rm]); + cpu->shifterOperand = cpu->shifterCarryOut; + } + } +} + +static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) { + int rm = opcode & 0x0000000F; + if (opcode & 0x00000010) { + int rs = (opcode >> 8) & 0x0000000F; + ++cpu->cycles; + int shift = cpu->gprs[rs]; + if (rs == ARM_PC) { + shift += 4; + } + shift &= 0xFF; + int shiftVal = cpu->gprs[rm]; + if (rm == ARM_PC) { + shiftVal += 4; + } + int rotate = shift & 0x1F; + if (!shift) { + cpu->shifterOperand = shiftVal; + cpu->shifterCarryOut = cpu->cpsr.c; + } else if (rotate) { + cpu->shifterOperand = ROR(shiftVal, rotate); + cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1; + } else { + cpu->shifterOperand = shiftVal; + cpu->shifterCarryOut = ARM_SIGN(shiftVal); + } + } else { + int immediate = (opcode & 0x00000F80) >> 7; + if (immediate) { + cpu->shifterOperand = ROR(cpu->gprs[rm], immediate); + cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1; + } else { + // RRX + cpu->shifterOperand = (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1); + cpu->shifterCarryOut = cpu->gprs[rm] & 0x00000001; + } + } +} + +static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) { + int rotate = (opcode & 0x00000F00) >> 7; + int immediate = opcode & 0x000000FF; + if (!rotate) { + cpu->shifterOperand = immediate; + cpu->shifterCarryOut = cpu->cpsr.c; + } else { + cpu->shifterOperand = ROR(immediate, rotate); + cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand); + } +} + +// Instruction definitions +// Beware pre-processor antics + +ATTRIBUTE_NOINLINE static void _additionS(struct ARMCore* cpu, int32_t m, int32_t n, int32_t d) { + cpu->cpsr.flags = 0; + cpu->cpsr.n = ARM_SIGN(d); + cpu->cpsr.z = !d; + cpu->cpsr.c = ARM_CARRY_FROM(m, n, d); + cpu->cpsr.v = ARM_V_ADDITION(m, n, d); +} + +ATTRIBUTE_NOINLINE static void _subtractionS(struct ARMCore* cpu, int32_t m, int32_t n, int32_t d) { + cpu->cpsr.flags = 0; + cpu->cpsr.n = ARM_SIGN(d); + cpu->cpsr.z = !d; + cpu->cpsr.c = ARM_BORROW_FROM(m, n, d); + cpu->cpsr.v = ARM_V_SUBTRACTION(m, n, d); +} + +ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) { + cpu->cpsr.n = ARM_SIGN(d); + cpu->cpsr.z = !d; \ + cpu->cpsr.c = cpu->shifterCarryOut; \ +} + +#define ARM_ADDITION_S(M, N, D) \ + if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \ + cpu->cpsr = cpu->spsr; \ + _ARMReadCPSR(cpu); \ + } else { \ + _additionS(cpu, M, N, D); \ + } + +#define ARM_SUBTRACTION_S(M, N, D) \ + if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \ + cpu->cpsr = cpu->spsr; \ + _ARMReadCPSR(cpu); \ + } else { \ + _subtractionS(cpu, M, N, D); \ + } + +#define ARM_SUBTRACTION_CARRY_S(M, N, D, C) \ + if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \ + cpu->cpsr = cpu->spsr; \ + _ARMReadCPSR(cpu); \ + } else { \ + cpu->cpsr.n = ARM_SIGN(D); \ + cpu->cpsr.z = !(D); \ + cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \ + cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D); \ + } + +#define ARM_NEUTRAL_S(M, N, D) \ + if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \ + cpu->cpsr = cpu->spsr; \ + _ARMReadCPSR(cpu); \ + } else { \ + _neutralS(cpu, D); \ + } + +#define ARM_NEUTRAL_HI_S(DLO, DHI) \ + cpu->cpsr.n = ARM_SIGN(DHI); \ + cpu->cpsr.z = !((DHI) | (DLO)); + +#define ADDR_MODE_2_I_TEST (opcode & 0x00000F80) +#define ADDR_MODE_2_I ((opcode & 0x00000F80) >> 7) +#define ADDR_MODE_2_ADDRESS (address) +#define ADDR_MODE_2_RN (cpu->gprs[rn]) +#define ADDR_MODE_2_RM (cpu->gprs[rm]) +#define ADDR_MODE_2_IMMEDIATE (opcode & 0x00000FFF) +#define ADDR_MODE_2_INDEX(U_OP, M) (cpu->gprs[rn] U_OP M) +#define ADDR_MODE_2_WRITEBACK(ADDR) \ + cpu->gprs[rn] = ADDR; \ + if (UNLIKELY(rn == ARM_PC)) { \ + currentCycles += ARMWritePC(cpu); \ + } + +#define ADDR_MODE_2_WRITEBACK_PRE_STORE(WB) +#define ADDR_MODE_2_WRITEBACK_POST_STORE(WB) WB +#define ADDR_MODE_2_WRITEBACK_PRE_LOAD(WB) WB +#define ADDR_MODE_2_WRITEBACK_POST_LOAD(WB) + +#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I) +#define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0) +#define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31) +#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1)) + +#define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS +#define ADDR_MODE_3_RN ADDR_MODE_2_RN +#define ADDR_MODE_3_RM ADDR_MODE_2_RM +#define ADDR_MODE_3_IMMEDIATE (((opcode & 0x00000F00) >> 4) | (opcode & 0x0000000F)) +#define ADDR_MODE_3_INDEX(U_OP, M) ADDR_MODE_2_INDEX(U_OP, M) +#define ADDR_MODE_3_WRITEBACK(ADDR) ADDR_MODE_2_WRITEBACK(ADDR) + +#define ADDR_MODE_4_WRITEBACK_LDM \ + if (!((1 << rn) & rs)) { \ + cpu->gprs[rn] = address; \ + } + +#define ADDR_MODE_4_WRITEBACK_STM cpu->gprs[rn] = address; + +#define ARM_LOAD_POST_BODY \ + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \ + if (rd == ARM_PC) { \ + currentCycles += ARMWritePC(cpu); \ + } + +#define ARM_STORE_POST_BODY \ + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; + +#define DEFINE_INSTRUCTION_ARM(NAME, BODY) \ + static void _ARMInstruction ## NAME (struct ARMCore* cpu, uint32_t opcode) { \ + int currentCycles = ARM_PREFETCH_CYCLES; \ + BODY; \ + cpu->cycles += currentCycles; \ + } + +#define DEFINE_ALU_INSTRUCTION_EX_ARM(NAME, S_BODY, SHIFTER, BODY) \ + DEFINE_INSTRUCTION_ARM(NAME, \ + int rd = (opcode >> 12) & 0xF; \ + int rn = (opcode >> 16) & 0xF; \ + UNUSED(rn); \ + SHIFTER(cpu, opcode); \ + BODY; \ + S_BODY; \ + if (rd == ARM_PC) { \ + currentCycles += ARMWritePC(cpu); \ + }) + +#define DEFINE_ALU_INSTRUCTION_ARM(NAME, S_BODY, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSL, , _shiftLSL, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_LSL, S_BODY, _shiftLSL, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSR, , _shiftLSR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_LSR, S_BODY, _shiftLSR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ASR, , _shiftASR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_ASR, S_BODY, _shiftASR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ROR, , _shiftROR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_ROR, S_BODY, _shiftROR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## I, , _immediate, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## SI, S_BODY, _immediate, BODY) + +#define DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(NAME, S_BODY, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSL, S_BODY, _shiftLSL, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSR, S_BODY, _shiftLSR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ASR, S_BODY, _shiftASR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ROR, S_BODY, _shiftROR, BODY) \ + DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## I, S_BODY, _immediate, BODY) + +#define DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME, BODY, S_BODY) \ + DEFINE_INSTRUCTION_ARM(NAME, \ + int rd = (opcode >> 16) & 0xF; \ + int rs = (opcode >> 8) & 0xF; \ + int rm = opcode & 0xF; \ + if (rd == ARM_PC) { \ + return; \ + } \ + ARM_WAIT_MUL(cpu->gprs[rs]); \ + BODY; \ + S_BODY; \ + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32) + +#define DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME, BODY, S_BODY, WAIT) \ + DEFINE_INSTRUCTION_ARM(NAME, \ + int rd = (opcode >> 12) & 0xF; \ + int rdHi = (opcode >> 16) & 0xF; \ + int rs = (opcode >> 8) & 0xF; \ + int rm = opcode & 0xF; \ + if (rdHi == ARM_PC || rd == ARM_PC) { \ + return; \ + } \ + currentCycles += cpu->memory.stall(cpu, WAIT); \ + BODY; \ + S_BODY; \ + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32) + +#define DEFINE_MULTIPLY_INSTRUCTION_ARM(NAME, BODY, S_BODY) \ + DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME, BODY, ) \ + DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME ## S, BODY, S_BODY) + +#define DEFINE_MULTIPLY_INSTRUCTION_2_ARM(NAME, BODY, S_BODY, WAIT) \ + DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME, BODY, , WAIT) \ + DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME ## S, BODY, S_BODY, WAIT) + +#define DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDRESS, WRITEBACK, LS, BODY) \ + DEFINE_INSTRUCTION_ARM(NAME, \ + uint32_t address; \ + int rn = (opcode >> 16) & 0xF; \ + int rd = (opcode >> 12) & 0xF; \ + int rm = opcode & 0xF; \ + UNUSED(rm); \ + address = ADDRESS; \ + ADDR_MODE_2_WRITEBACK_PRE_ ## LS (WRITEBACK); \ + BODY; \ + ADDR_MODE_2_WRITEBACK_POST_ ## LS (WRITEBACK);) + +#define DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME, SHIFTER, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, SHIFTER)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, SHIFTER)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## P, ADDR_MODE_2_INDEX(-, SHIFTER), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PW, ADDR_MODE_2_INDEX(-, SHIFTER), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PU, ADDR_MODE_2_INDEX(+, SHIFTER), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PUW, ADDR_MODE_2_INDEX(+, SHIFTER), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) + +#define DEFINE_LOAD_STORE_INSTRUCTION_ARM(NAME, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _LSL_, ADDR_MODE_2_LSL, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _LSR_, ADDR_MODE_2_LSR, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _ASR_, ADDR_MODE_2_ASR, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IP, ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPW, ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPU, ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPUW, ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \ + +#define DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(NAME, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## P, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PW, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PU, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PUW, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IP, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPW, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPU, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE), , LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPUW, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \ + +#define DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME, SHIFTER, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, SHIFTER, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_RM)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, SHIFTER, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_RM)), LS, BODY) \ + +#define DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(NAME, LS, BODY) \ + DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _LSL_, ADDR_MODE_2_LSL, LS, BODY) \ + DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _LSR_, ADDR_MODE_2_LSR, LS, BODY) \ + DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ASR_, ADDR_MODE_2_ASR, LS, BODY) \ + DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \ + DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \ + +#define ARM_MS_PRE(IS_LOAD) \ + enum PrivilegeMode privilegeMode = cpu->privilegeMode; \ + if (!(opcode & 0x8000) || !IS_LOAD) ARMSetPrivilegeMode(cpu, MODE_USER); + +#define ARM_MS_POST(IS_LOAD) \ + ARMSetPrivilegeMode(cpu, privilegeMode); \ + if (IS_LOAD && (opcode & 0x8000)) { \ + cpu->cpsr.packed = cpu->spsr.packed;\ + ARMSetPrivilegeMode(cpu, cpu->cpsr.priv);\ + } + +#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME, LS, WRITEBACK, S_PRE, S_POST, DIRECTION, POST_BODY) \ + DEFINE_INSTRUCTION_ARM(NAME, \ + int rn = (opcode >> 16) & 0xF; \ + int rs = opcode & 0x0000FFFF; \ + uint32_t address = cpu->gprs[rn]; \ + S_PRE; \ + address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, ¤tCycles); \ + POST_BODY; \ + WRITEBACK; \ + S_POST; \ + ) + + +#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(NAME, LS, IS_LOAD, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DA, LS, , , , DA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , DA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DB, LS, , , , DB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , DB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IA, LS, , , , IA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IB, LS, , , , IB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IA, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IB, POST_BODY) \ + DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IB, POST_BODY) + +// Begin ALU definitions + +DEFINE_ALU_INSTRUCTION_ARM(ADD, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]), + int32_t n = cpu->gprs[rn]; + cpu->gprs[rd] = n + cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(ADC, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]), + int32_t n = cpu->gprs[rn]; + cpu->gprs[rd] = n + cpu->shifterOperand + cpu->cpsr.c;) + +DEFINE_ALU_INSTRUCTION_ARM(AND, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]), + cpu->gprs[rd] = cpu->gprs[rn] & cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(BIC, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]), + cpu->gprs[rd] = cpu->gprs[rn] & ~cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMN, ARM_ADDITION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut), + int32_t aluOut = cpu->gprs[rn] + cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMP, ARM_SUBTRACTION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut), + int32_t aluOut = cpu->gprs[rn] - cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(EOR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]), + cpu->gprs[rd] = cpu->gprs[rn] ^ cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(MOV, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]), + cpu->gprs[rd] = cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(MVN, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]), + cpu->gprs[rd] = ~cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(ORR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]), + cpu->gprs[rd] = cpu->gprs[rn] | cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_ARM(RSB, ARM_SUBTRACTION_S(cpu->shifterOperand, n, cpu->gprs[rd]), + int32_t n = cpu->gprs[rn]; + cpu->gprs[rd] = cpu->shifterOperand - n;) + +DEFINE_ALU_INSTRUCTION_ARM(RSC, ARM_SUBTRACTION_CARRY_S(cpu->shifterOperand, n, cpu->gprs[rd], !cpu->cpsr.c), + int32_t n = cpu->gprs[rn]; + cpu->gprs[rd] = cpu->shifterOperand - n - !cpu->cpsr.c;) + +DEFINE_ALU_INSTRUCTION_ARM(SBC, ARM_SUBTRACTION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], !cpu->cpsr.c), + int32_t n = cpu->gprs[rn]; + cpu->gprs[rd] = n - cpu->shifterOperand - !cpu->cpsr.c;) + +DEFINE_ALU_INSTRUCTION_ARM(SUB, ARM_SUBTRACTION_S(n, cpu->shifterOperand, cpu->gprs[rd]), + int32_t n = cpu->gprs[rn]; + cpu->gprs[rd] = n - cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TEQ, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut), + int32_t aluOut = cpu->gprs[rn] ^ cpu->shifterOperand;) + +DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TST, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut), + int32_t aluOut = cpu->gprs[rn] & cpu->shifterOperand;) + +// End ALU definitions + +// Begin multiply definitions + +DEFINE_MULTIPLY_INSTRUCTION_2_ARM(MLA, cpu->gprs[rdHi] = cpu->gprs[rm] * cpu->gprs[rs] + cpu->gprs[rd], ARM_NEUTRAL_S(, , cpu->gprs[rdHi]), 2) +DEFINE_MULTIPLY_INSTRUCTION_ARM(MUL, cpu->gprs[rd] = cpu->gprs[rm] * cpu->gprs[rs], ARM_NEUTRAL_S(cpu->gprs[rm], cpu->gprs[rs], cpu->gprs[rd])) + +DEFINE_MULTIPLY_INSTRUCTION_2_ARM(SMLAL, + int64_t d = ((int64_t) cpu->gprs[rm]) * ((int64_t) cpu->gprs[rs]); + int32_t dm = cpu->gprs[rd]; + int32_t dn = d; + cpu->gprs[rd] = dm + dn; + cpu->gprs[rdHi] = cpu->gprs[rdHi] + (d >> 32) + ARM_CARRY_FROM(dm, dn, cpu->gprs[rd]);, + ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 3) + +DEFINE_MULTIPLY_INSTRUCTION_2_ARM(SMULL, + int64_t d = ((int64_t) cpu->gprs[rm]) * ((int64_t) cpu->gprs[rs]); + cpu->gprs[rd] = d; + cpu->gprs[rdHi] = d >> 32;, + ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 2) + +DEFINE_MULTIPLY_INSTRUCTION_2_ARM(UMLAL, + uint64_t d = ARM_UXT_64(cpu->gprs[rm]) * ARM_UXT_64(cpu->gprs[rs]); + int32_t dm = cpu->gprs[rd]; + int32_t dn = d; + cpu->gprs[rd] = dm + dn; + cpu->gprs[rdHi] = cpu->gprs[rdHi] + (d >> 32) + ARM_CARRY_FROM(dm, dn, cpu->gprs[rd]);, + ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 3) + +DEFINE_MULTIPLY_INSTRUCTION_2_ARM(UMULL, + uint64_t d = ARM_UXT_64(cpu->gprs[rm]) * ARM_UXT_64(cpu->gprs[rs]); + cpu->gprs[rd] = d; + cpu->gprs[rdHi] = d >> 32;, + ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 2) + +// End multiply definitions + +// Begin load/store definitions + +DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, LOAD, cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, LOAD, cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, LOAD, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, LOAD, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, LOAD, cpu->gprs[rd] = address & 1 ? ARM_SXT_8(cpu->memory.load16(cpu, address, ¤tCycles)) : ARM_SXT_16(cpu->memory.load16(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, STORE, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, STORE, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, STORE, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;) + +DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT, LOAD, + enum PrivilegeMode priv = cpu->privilegeMode; + ARMSetPrivilegeMode(cpu, MODE_USER); + int32_t r = cpu->memory.load8(cpu, address, ¤tCycles); + ARMSetPrivilegeMode(cpu, priv); + cpu->gprs[rd] = r; + ARM_LOAD_POST_BODY;) + +DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRT, LOAD, + enum PrivilegeMode priv = cpu->privilegeMode; + ARMSetPrivilegeMode(cpu, MODE_USER); + int32_t r = cpu->memory.load32(cpu, address, ¤tCycles); + ARMSetPrivilegeMode(cpu, priv); + cpu->gprs[rd] = r; + ARM_LOAD_POST_BODY;) + +DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRBT, STORE, + enum PrivilegeMode priv = cpu->privilegeMode; + int32_t r = cpu->gprs[rd]; + ARMSetPrivilegeMode(cpu, MODE_USER); + cpu->memory.store8(cpu, address, r, ¤tCycles); + ARMSetPrivilegeMode(cpu, priv); + ARM_STORE_POST_BODY;) + +DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRT, STORE, + enum PrivilegeMode priv = cpu->privilegeMode; + int32_t r = cpu->gprs[rd]; + ARMSetPrivilegeMode(cpu, MODE_USER); + cpu->memory.store32(cpu, address, r, ¤tCycles); + ARMSetPrivilegeMode(cpu, priv); + ARM_STORE_POST_BODY;) + +DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM, + load, true, + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; + if ((rs & 0x8000) || !rs) { + currentCycles += ARMWritePC(cpu); + }) + +DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(STM, + store, false, + ARM_STORE_POST_BODY;) + +DEFINE_INSTRUCTION_ARM(SWP, + int rm = opcode & 0xF; + int rd = (opcode >> 12) & 0xF; + int rn = (opcode >> 16) & 0xF; + int32_t d = cpu->memory.load32(cpu, cpu->gprs[rn], ¤tCycles); + cpu->memory.store32(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles); + cpu->gprs[rd] = d;) + +DEFINE_INSTRUCTION_ARM(SWPB, + int rm = opcode & 0xF; + int rd = (opcode >> 12) & 0xF; + int rn = (opcode >> 16) & 0xF; + int32_t d = cpu->memory.load8(cpu, cpu->gprs[rn], ¤tCycles); + cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles); + cpu->gprs[rd] = d;) + +// End load/store definitions + +// Begin branch definitions + +DEFINE_INSTRUCTION_ARM(B, + int32_t offset = opcode << 8; + offset >>= 6; + cpu->gprs[ARM_PC] += offset; + currentCycles += ARMWritePC(cpu);) + +DEFINE_INSTRUCTION_ARM(BL, + int32_t immediate = (opcode & 0x00FFFFFF) << 8; + cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4; + cpu->gprs[ARM_PC] += immediate >> 6; + currentCycles += ARMWritePC(cpu);) + +DEFINE_INSTRUCTION_ARM(BX, + int rm = opcode & 0x0000000F; + cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE; + currentCycles += ARMWritePC(cpu); + ) + +// End branch definitions + +// Begin coprocessor definitions + +DEFINE_INSTRUCTION_ARM(CDP, ARM_STUB) +DEFINE_INSTRUCTION_ARM(LDC, ARM_STUB) +DEFINE_INSTRUCTION_ARM(STC, ARM_STUB) +DEFINE_INSTRUCTION_ARM(MCR, ARM_STUB) +DEFINE_INSTRUCTION_ARM(MRC, ARM_STUB) + +// Begin miscellaneous definitions + +DEFINE_INSTRUCTION_ARM(BKPT, cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF))); // Not strictly in ARMv4T, but here for convenience +DEFINE_INSTRUCTION_ARM(ILL, ARM_ILL) // Illegal opcode + +DEFINE_INSTRUCTION_ARM(MSR, + int c = opcode & 0x00010000; + int f = opcode & 0x00080000; + int32_t operand = cpu->gprs[opcode & 0x0000000F]; + int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0); + if (mask & PSR_USER_MASK) { + cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK); + } + if (mask & PSR_STATE_MASK) { + cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK); + } + if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) { + ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010)); + cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK); + } + _ARMReadCPSR(cpu); + // LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - 4) & cpu->memory.activeMask, cpu->memory.activeRegion); + // LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); + cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC] - 4, NULL); + cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL); + ) + +DEFINE_INSTRUCTION_ARM(MSRR, + int c = opcode & 0x00010000; + int f = opcode & 0x00080000; + int32_t operand = cpu->gprs[opcode & 0x0000000F]; + int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0); + mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK; + cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;) + +DEFINE_INSTRUCTION_ARM(MRS, \ + int rd = (opcode >> 12) & 0xF; \ + cpu->gprs[rd] = cpu->cpsr.packed;) + +DEFINE_INSTRUCTION_ARM(MRSR, \ + int rd = (opcode >> 12) & 0xF; \ + cpu->gprs[rd] = cpu->spsr.packed;) + +DEFINE_INSTRUCTION_ARM(MSRI, + int c = opcode & 0x00010000; + int f = opcode & 0x00080000; + int rotate = (opcode & 0x00000F00) >> 7; + int32_t operand = ROR(opcode & 0x000000FF, rotate); + int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0); + if (mask & PSR_USER_MASK) { + cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK); + } + if (mask & PSR_STATE_MASK) { + cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK); + } + if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) { + ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010)); + cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK); + } + _ARMReadCPSR(cpu); + // LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - 4) & cpu->memory.activeMask, cpu->memory.activeRegion); + // LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); + cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC] - 4, NULL); + cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL); + ) + +DEFINE_INSTRUCTION_ARM(MSRRI, + int c = opcode & 0x00010000; + int f = opcode & 0x00080000; + int rotate = (opcode & 0x00000F00) >> 7; + int32_t operand = ROR(opcode & 0x000000FF, rotate); + int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0); + mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK; + cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;) + +DEFINE_INSTRUCTION_ARM(SWI, cpu->irqh.swi32(cpu, opcode & 0xFFFFFF)) + +const ARMInstruction _armTable[0x1000] = { + DECLARE_ARM_EMITTER_BLOCK(_ARMInstruction) +}; diff --git a/WindCore/isa-arm.h b/WindCore/isa-arm.h new file mode 100644 index 0000000..fc4351b --- /dev/null +++ b/WindCore/isa-arm.h @@ -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 diff --git a/WindCore/isa-inlines.h b/WindCore/isa-inlines.h new file mode 100644 index 0000000..2e2ab66 --- /dev/null +++ b/WindCore/isa-inlines.h @@ -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 diff --git a/WindCore/macros.h b/WindCore/macros.h new file mode 100644 index 0000000..21e8498 --- /dev/null +++ b/WindCore/macros.h @@ -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 diff --git a/WindCore/wind.cpp b/WindCore/wind.cpp new file mode 100644 index 0000000..d8cbbe3 --- /dev/null +++ b/WindCore/wind.cpp @@ -0,0 +1,57 @@ +#include "wind.h" +#include + +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); +} diff --git a/WindCore/wind.h b/WindCore/wind.h new file mode 100644 index 0000000..18fe7ab --- /dev/null +++ b/WindCore/wind.h @@ -0,0 +1,114 @@ +#include +#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); diff --git a/WindCore/wind_hw.h b/WindCore/wind_hw.h new file mode 100644 index 0000000..ed4c9a3 --- /dev/null +++ b/WindCore/wind_hw.h @@ -0,0 +1,153 @@ +#pragma once +#include "wind.h" +#include + +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]); + } + } +}; diff --git a/WindEmu.pro b/WindEmu.pro new file mode 100644 index 0000000..7cc6ae7 --- /dev/null +++ b/WindEmu.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + WindQt \ + WindCore diff --git a/WindQt/WindQt.pro b/WindQt/WindQt.pro new file mode 100644 index 0000000..5612e6a --- /dev/null +++ b/WindQt/WindQt.pro @@ -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 diff --git a/WindQt/main.cpp b/WindQt/main.cpp new file mode 100644 index 0000000..b48f94e --- /dev/null +++ b/WindQt/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/WindQt/mainwindow.cpp b/WindQt/mainwindow.cpp new file mode 100644 index 0000000..0dd9d20 --- /dev/null +++ b/WindQt/mainwindow.cpp @@ -0,0 +1,171 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "../WindCore/wind.h" +#include +#include + +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; +} diff --git a/WindQt/mainwindow.h b/WindQt/mainwindow.h new file mode 100644 index 0000000..6059f6d --- /dev/null +++ b/WindQt/mainwindow.h @@ -0,0 +1,35 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#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 diff --git a/WindQt/mainwindow.ui b/WindQt/mainwindow.ui new file mode 100644 index 0000000..43a46e0 --- /dev/null +++ b/WindQt/mainwindow.ui @@ -0,0 +1,58 @@ + + + MainWindow + + + + 0 + 0 + 752 + 429 + + + + MainWindow + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Step + + + + + + + Cycles + + + + + + + + + + + + + + + + + -- 2.45.2