572 lines
21 KiB
C++
572 lines
21 KiB
C++
#include <cstdint>
|
|
#include <QtCore>
|
|
#include <QJsonDocument>
|
|
#include <cassert>
|
|
#include <bitset>
|
|
#include <limits>
|
|
|
|
#include "Avr8.hpp"
|
|
#include "PadDescriptor.hpp"
|
|
#include "PhysicalInterface.hpp"
|
|
#include "src/Logger/Logger.hpp"
|
|
#include "src/Exceptions/InvalidConfig.hpp"
|
|
#include "src/Targets/TargetRegister.hpp"
|
|
#include "src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp"
|
|
|
|
// Derived AVR8 targets
|
|
#include "XMega/XMega.hpp"
|
|
#include "Mega/Mega.hpp"
|
|
#include "Tiny/Tiny.hpp"
|
|
|
|
using namespace Bloom;
|
|
using namespace Bloom::Targets;
|
|
using namespace Bloom::Targets::Microchip::Avr;
|
|
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit;
|
|
using namespace Exceptions;
|
|
|
|
void Avr8::loadTargetDescriptionFile() {
|
|
this->targetDescriptionFile = TargetDescription::TargetDescriptionFile(
|
|
this->getId(),
|
|
(!this->name.empty()) ? std::optional(this->name) : std::nullopt
|
|
);
|
|
}
|
|
|
|
void Avr8::initFromTargetDescriptionFile() {
|
|
assert(this->targetDescriptionFile.has_value());
|
|
this->name = this->targetDescriptionFile->getTargetName();
|
|
this->family = this->targetDescriptionFile->getFamily();
|
|
|
|
this->targetParameters = this->targetDescriptionFile->getTargetParameters();
|
|
this->padDescriptorsByName = this->targetDescriptionFile->getPadDescriptorsMappedByName();
|
|
this->targetVariantsById = this->targetDescriptionFile->getVariantsMappedById();
|
|
|
|
if (!this->targetParameters->stackPointerRegisterLowAddress.has_value()) {
|
|
throw Exception("Failed to load sufficient AVR8 target paramters - missting stack pointer start address");
|
|
}
|
|
|
|
if (!this->targetParameters->statusRegisterStartAddress.has_value()) {
|
|
throw Exception("Failed to load sufficient AVR8 target parameters - missting status register start address");
|
|
}
|
|
|
|
this->loadTargetRegisterDescriptors();
|
|
}
|
|
|
|
void Avr8::loadTargetRegisterDescriptors() {
|
|
this->targetRegisterDescriptorsByType = this->targetDescriptionFile->getRegisterDescriptorsMappedByType();
|
|
|
|
/*
|
|
* All AVR8 targets possess 32 general purpose CPU registers. These are not described in the TDF, so we
|
|
* construct the descriptors for them here.
|
|
*/
|
|
auto gpRegisterStartAddress = this->targetParameters->gpRegisterStartAddress.value_or(0);
|
|
for (std::uint8_t i = 0; i <= 31; i++) {
|
|
auto generalPurposeRegisterDescriptor = TargetRegisterDescriptor();
|
|
generalPurposeRegisterDescriptor.startAddress = gpRegisterStartAddress + i;
|
|
generalPurposeRegisterDescriptor.size = 1;
|
|
generalPurposeRegisterDescriptor.type = TargetRegisterType::GENERAL_PURPOSE_REGISTER;
|
|
generalPurposeRegisterDescriptor.name = "R" + std::to_string(i);
|
|
generalPurposeRegisterDescriptor.groupName = "General Purpose CPU Registers";
|
|
|
|
this->targetRegisterDescriptorsByType[generalPurposeRegisterDescriptor.type].emplace_back(
|
|
generalPurposeRegisterDescriptor
|
|
);
|
|
}
|
|
|
|
/*
|
|
* The SP and SREG registers are described in the TDF, so we could just use the descriptors extracted from the
|
|
* TDF. The problem with that is, sometimes the SP register consists of two bytes; an SPL and an SPH. These need
|
|
* to be combined into one register descriptor. This is why we just use what we already have in
|
|
* this->targetParameters.
|
|
*/
|
|
auto stackPointerRegisterDescriptor = TargetRegisterDescriptor();
|
|
stackPointerRegisterDescriptor.type = TargetRegisterType::STACK_POINTER;
|
|
stackPointerRegisterDescriptor.startAddress = this->targetParameters->stackPointerRegisterLowAddress.value();
|
|
stackPointerRegisterDescriptor.size = this->targetParameters->stackPointerRegisterSize.value();
|
|
stackPointerRegisterDescriptor.name = "SP";
|
|
stackPointerRegisterDescriptor.groupName = "CPU";
|
|
stackPointerRegisterDescriptor.description = "Stack Pointer Register";
|
|
|
|
auto statusRegisterDescriptor = TargetRegisterDescriptor();
|
|
statusRegisterDescriptor.type = TargetRegisterType::STATUS_REGISTER;
|
|
statusRegisterDescriptor.startAddress = this->targetParameters->statusRegisterStartAddress.value();
|
|
statusRegisterDescriptor.size = this->targetParameters->statusRegisterSize.value();
|
|
statusRegisterDescriptor.name = "SREG";
|
|
statusRegisterDescriptor.groupName = "CPU";
|
|
statusRegisterDescriptor.description = "Status Register";
|
|
|
|
auto programCounterRegisterDescriptor = TargetRegisterDescriptor();
|
|
programCounterRegisterDescriptor.type = TargetRegisterType::PROGRAM_COUNTER;
|
|
programCounterRegisterDescriptor.size = 4;
|
|
programCounterRegisterDescriptor.name = "PC";
|
|
programCounterRegisterDescriptor.groupName = "CPU";
|
|
programCounterRegisterDescriptor.description = "Program Counter";
|
|
|
|
this->targetRegisterDescriptorsByType[stackPointerRegisterDescriptor.type].emplace_back(
|
|
stackPointerRegisterDescriptor
|
|
);
|
|
this->targetRegisterDescriptorsByType[statusRegisterDescriptor.type].emplace_back(
|
|
statusRegisterDescriptor
|
|
);
|
|
this->targetRegisterDescriptorsByType[programCounterRegisterDescriptor.type].emplace_back(
|
|
programCounterRegisterDescriptor
|
|
);
|
|
}
|
|
|
|
TargetSignature Avr8::getId() {
|
|
if (!this->id.has_value()) {
|
|
this->id = this->avr8Interface->getDeviceId();
|
|
}
|
|
|
|
return this->id.value();
|
|
}
|
|
|
|
void Avr8::preActivationConfigure(const TargetConfig& targetConfig) {
|
|
Target::preActivationConfigure(targetConfig);
|
|
|
|
if (this->family.has_value()) {
|
|
this->avr8Interface->setFamily(this->family.value());
|
|
}
|
|
|
|
this->avr8Interface->configure(targetConfig);
|
|
}
|
|
|
|
void Avr8::postActivationConfigure() {
|
|
if (!this->targetDescriptionFile.has_value()) {
|
|
this->loadTargetDescriptionFile();
|
|
this->initFromTargetDescriptionFile();
|
|
}
|
|
|
|
/*
|
|
* The signature obtained from the device should match what is in the target description file
|
|
*
|
|
* We don't use this->getId() here as that could return the ID that was extracted from the target description file
|
|
* (which it would, if the user specified the exact target name in their project config - see Avr8::getId() and
|
|
* TargetController::getSupportedTargets() for more).
|
|
*/
|
|
auto targetSignature = this->avr8Interface->getDeviceId();
|
|
auto tdSignature = this->targetDescriptionFile->getTargetSignature();
|
|
|
|
if (targetSignature != tdSignature) {
|
|
throw Exception("Failed to validate connected target - target signature mismatch.\nThe target signature"
|
|
" (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\""
|
|
+ tdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the configuration file"
|
|
+ " (bloom.json)."
|
|
);
|
|
}
|
|
}
|
|
|
|
void Avr8::postPromotionConfigure() {
|
|
if (!this->family.has_value()) {
|
|
throw Exception("Failed to resolve AVR8 family");
|
|
}
|
|
|
|
this->avr8Interface->setFamily(this->family.value());
|
|
this->avr8Interface->setTargetParameters(this->targetParameters.value());
|
|
}
|
|
|
|
void Avr8::activate() {
|
|
if (this->isActivated()) {
|
|
return;
|
|
}
|
|
|
|
this->avr8Interface->init();
|
|
|
|
if (this->targetDescriptionFile.has_value()) {
|
|
this->avr8Interface->setTargetParameters(this->targetParameters.value());
|
|
}
|
|
|
|
this->avr8Interface->activate();
|
|
this->activated = true;
|
|
}
|
|
|
|
std::unique_ptr<Targets::Target> Avr8::promote() {
|
|
std::unique_ptr<Targets::Target> promoted = nullptr;
|
|
|
|
if (this->family.has_value()) {
|
|
// Promote generic AVR8 target to correct family.
|
|
switch (this->family.value()) {
|
|
case Family::XMEGA: {
|
|
Logger::info("AVR8 target promoted to XMega target");
|
|
promoted = std::make_unique<XMega>(*this);
|
|
break;
|
|
}
|
|
case Family::MEGA: {
|
|
Logger::info("AVR8 target promoted to megaAVR target");
|
|
promoted = std::make_unique<Mega>(*this);
|
|
break;
|
|
}
|
|
case Family::TINY: {
|
|
Logger::info("AVR8 target promoted to tinyAVR target");
|
|
promoted = std::make_unique<Tiny>(*this);
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return promoted;
|
|
}
|
|
|
|
void Avr8::deactivate() {
|
|
try {
|
|
this->avr8Interface->deactivate();
|
|
this->activated = false;
|
|
|
|
} catch (const Exception& exception) {
|
|
Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage());
|
|
}
|
|
}
|
|
|
|
TargetDescriptor Avr8Bit::Avr8::getDescriptor() {
|
|
auto descriptor = TargetDescriptor();
|
|
descriptor.id = this->getHumanReadableId();
|
|
descriptor.name = this->getName();
|
|
descriptor.ramSize = this->targetParameters.value().ramSize.value_or(0);
|
|
descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType;
|
|
|
|
std::transform(
|
|
this->targetVariantsById.begin(),
|
|
this->targetVariantsById.end(),
|
|
std::back_inserter(descriptor.variants),
|
|
[](auto& variantToIdPair) {
|
|
return variantToIdPair.second;
|
|
}
|
|
);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
void Avr8::run() {
|
|
this->avr8Interface->run();
|
|
}
|
|
|
|
void Avr8::stop() {
|
|
this->avr8Interface->stop();
|
|
}
|
|
|
|
void Avr8::step() {
|
|
this->avr8Interface->step();
|
|
}
|
|
|
|
void Avr8::reset() {
|
|
this->avr8Interface->reset();
|
|
}
|
|
|
|
void Avr8::setBreakpoint(std::uint32_t address) {
|
|
this->avr8Interface->setBreakpoint(address);
|
|
}
|
|
|
|
void Avr8::removeBreakpoint(std::uint32_t address) {
|
|
this->avr8Interface->clearBreakpoint(address);
|
|
}
|
|
|
|
void Avr8::clearAllBreakpoints() {
|
|
this->avr8Interface->clearAllBreakpoints();
|
|
}
|
|
|
|
void Avr8::writeRegisters(TargetRegisters registers) {
|
|
for (auto registerIt = registers.begin(); registerIt != registers.end();) {
|
|
if (registerIt->descriptor.type == TargetRegisterType::PROGRAM_COUNTER) {
|
|
auto programCounterBytes = registerIt->value;
|
|
|
|
if (programCounterBytes.size() < 4) {
|
|
// All PC register values should be at least 4 bytes in size
|
|
programCounterBytes.insert(programCounterBytes.begin(), 4 - programCounterBytes.size(), 0x00);
|
|
}
|
|
|
|
this->setProgramCounter(static_cast<std::uint32_t>(
|
|
programCounterBytes[0] << 24
|
|
| programCounterBytes[1] << 16
|
|
| programCounterBytes[2] << 8
|
|
| programCounterBytes[3]
|
|
));
|
|
|
|
registerIt = registers.erase(registerIt);
|
|
|
|
} else {
|
|
registerIt++;
|
|
}
|
|
}
|
|
|
|
if (!registers.empty()) {
|
|
this->avr8Interface->writeRegisters(registers);
|
|
}
|
|
}
|
|
|
|
TargetRegisters Avr8::readRegisters(TargetRegisterDescriptors descriptors) {
|
|
TargetRegisters registers;
|
|
|
|
for (auto registerDescriptorIt = descriptors.begin(); registerDescriptorIt != descriptors.end();) {
|
|
auto& descriptor = *registerDescriptorIt;
|
|
|
|
if (descriptor.type == TargetRegisterType::PROGRAM_COUNTER) {
|
|
registers.push_back(this->getProgramCounterRegister());
|
|
|
|
registerDescriptorIt = descriptors.erase(registerDescriptorIt);
|
|
|
|
} else {
|
|
registerDescriptorIt++;
|
|
}
|
|
}
|
|
|
|
if (!descriptors.empty()) {
|
|
auto otherRegisters = this->avr8Interface->readRegisters(descriptors);
|
|
registers.insert(registers.end(), otherRegisters.begin(), otherRegisters.end());
|
|
}
|
|
|
|
return registers;
|
|
}
|
|
|
|
TargetMemoryBuffer Avr8::readMemory(TargetMemoryType memoryType, std::uint32_t startAddress, std::uint32_t bytes) {
|
|
return this->avr8Interface->readMemory(memoryType, startAddress, bytes);
|
|
}
|
|
|
|
void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) {
|
|
this->avr8Interface->writeMemory(memoryType, startAddress, buffer);
|
|
}
|
|
|
|
TargetState Avr8::getState() {
|
|
return this->avr8Interface->getTargetState();
|
|
}
|
|
|
|
std::uint32_t Avr8::getProgramCounter() {
|
|
return this->avr8Interface->getProgramCounter();
|
|
}
|
|
|
|
TargetRegister Avr8::getProgramCounterRegister() {
|
|
auto programCounter = this->getProgramCounter();
|
|
|
|
return TargetRegister(TargetRegisterDescriptor(TargetRegisterType::PROGRAM_COUNTER), {
|
|
static_cast<unsigned char>(programCounter >> 24),
|
|
static_cast<unsigned char>(programCounter >> 16),
|
|
static_cast<unsigned char>(programCounter >> 8),
|
|
static_cast<unsigned char>(programCounter),
|
|
});
|
|
}
|
|
|
|
void Avr8::setProgramCounter(std::uint32_t programCounter) {
|
|
this->avr8Interface->setProgramCounter(programCounter);
|
|
}
|
|
|
|
std::map<int, TargetPinState> Avr8::getPinStates(int variantId) {
|
|
if (!this->targetVariantsById.contains(variantId)) {
|
|
throw Exception("Invalid target variant ID");
|
|
}
|
|
|
|
std::map<int, TargetPinState> output;
|
|
auto& variant = this->targetVariantsById.at(variantId);
|
|
|
|
/*
|
|
* To prevent the number of memory reads we perform here, we cache the data and map it by start address.
|
|
*
|
|
* This way, we only perform 3 memory reads for a target variant with 3 ports - one per port (instead of one
|
|
* per pin).
|
|
*
|
|
* We may be able to make this more efficient by combining reads for ports with aligned memory addresses. This will
|
|
* be considered when the need for it becomes apparent.
|
|
*/
|
|
std::map<std::uint16_t, TargetMemoryBuffer> cachedMemoryByStartAddress;
|
|
auto readMemoryBitset = [this, &cachedMemoryByStartAddress](std::uint16_t startAddress) {
|
|
if (!cachedMemoryByStartAddress.contains(startAddress)) {
|
|
cachedMemoryByStartAddress.insert(
|
|
std::pair(
|
|
startAddress,
|
|
this->readMemory(TargetMemoryType::RAM, startAddress, 1)
|
|
)
|
|
);
|
|
}
|
|
|
|
return std::bitset<std::numeric_limits<unsigned char>::digits>(
|
|
cachedMemoryByStartAddress.at(startAddress).at(0)
|
|
);
|
|
};
|
|
|
|
for (const auto& [pinNumber, pinDescriptor] : variant.pinDescriptorsByNumber) {
|
|
if (this->padDescriptorsByName.contains(pinDescriptor.padName)) {
|
|
auto& pad = this->padDescriptorsByName.at(pinDescriptor.padName);
|
|
|
|
if (!pad.gpioPinNumber.has_value()) {
|
|
continue;
|
|
}
|
|
|
|
auto pinState = TargetPinState();
|
|
|
|
if (pad.ddrSetAddress.has_value()) {
|
|
auto dataDirectionRegisterValue = readMemoryBitset(pad.ddrSetAddress.value());
|
|
pinState.ioDirection = dataDirectionRegisterValue.test(pad.gpioPinNumber.value()) ?
|
|
TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT;
|
|
|
|
if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT && pad.gpioPortSetAddress.has_value()) {
|
|
auto portRegisterValue = readMemoryBitset(pad.gpioPortSetAddress.value());
|
|
pinState.ioState = portRegisterValue.test(pad.gpioPinNumber.value()) ?
|
|
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
|
|
|
|
} else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT
|
|
&& pad.gpioPortInputAddress.has_value()
|
|
) {
|
|
auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value());
|
|
auto h = portInputRegisterValue.to_string();
|
|
pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ?
|
|
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
|
|
}
|
|
}
|
|
|
|
output.insert(std::pair(pinNumber, pinState));
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
void Avr8::setPinState(int variantId, const TargetPinDescriptor& pinDescriptor, const TargetPinState& state) {
|
|
if (!this->targetVariantsById.contains(variantId)) {
|
|
throw Exception("Invalid target variant ID");
|
|
}
|
|
|
|
if (!this->padDescriptorsByName.contains(pinDescriptor.padName)) {
|
|
throw Exception("Unknown pad");
|
|
}
|
|
|
|
if (!state.ioDirection.has_value()) {
|
|
throw Exception("Missing IO direction state");
|
|
}
|
|
|
|
auto& variant = this->targetVariantsById.at(variantId);
|
|
auto& padDescriptor = this->padDescriptorsByName.at(pinDescriptor.padName);
|
|
auto ioState = state.ioState;
|
|
|
|
if (state.ioDirection == TargetPinState::IoDirection::INPUT) {
|
|
// When setting the direction to INPUT, we must always set the IO pinstate to LOW
|
|
ioState = TargetPinState::IoState::LOW;
|
|
}
|
|
|
|
if (!padDescriptor.ddrSetAddress.has_value()
|
|
|| !padDescriptor.gpioPortSetAddress.has_value()
|
|
|| !padDescriptor.gpioPinNumber.has_value()
|
|
) {
|
|
throw Exception("Inadequate pad descriptor");
|
|
}
|
|
|
|
auto pinNumber = padDescriptor.gpioPinNumber.value();
|
|
auto ddrSetAddress = padDescriptor.ddrSetAddress.value();
|
|
auto ddrSetValue = this->readMemory(TargetMemoryType::RAM, ddrSetAddress, 1);
|
|
|
|
if (ddrSetValue.empty()) {
|
|
throw Exception("Failed to read DDSR value");
|
|
}
|
|
|
|
auto ddrSetBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrSetValue.front());
|
|
if (ddrSetBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) {
|
|
// DDR needs updating
|
|
ddrSetBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT));
|
|
|
|
this->writeMemory(
|
|
TargetMemoryType::RAM,
|
|
ddrSetAddress,
|
|
{static_cast<unsigned char>(ddrSetBitset.to_ulong())}
|
|
);
|
|
}
|
|
|
|
if (padDescriptor.ddrClearAddress.has_value() && padDescriptor.ddrClearAddress != ddrSetAddress) {
|
|
// We also need to ensure the data direction clear register value is correct
|
|
auto ddrClearAddress = padDescriptor.ddrClearAddress.value();
|
|
auto ddrClearValue = this->readMemory(TargetMemoryType::RAM, ddrClearAddress, 1);
|
|
|
|
if (ddrClearValue.empty()) {
|
|
throw Exception("Failed to read DDCR value");
|
|
}
|
|
|
|
auto ddrClearBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrClearValue.front());
|
|
if (ddrClearBitset.test(pinNumber) == (state.ioDirection == TargetPinState::IoDirection::INPUT)) {
|
|
ddrClearBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::INPUT));
|
|
|
|
this->writeMemory(
|
|
TargetMemoryType::RAM,
|
|
ddrClearAddress,
|
|
{static_cast<unsigned char>(ddrClearBitset.to_ulong())}
|
|
);
|
|
}
|
|
}
|
|
|
|
if (ioState.has_value()) {
|
|
auto portSetAddress = padDescriptor.gpioPortSetAddress.value();
|
|
|
|
if (ioState == TargetPinState::IoState::HIGH
|
|
|| !padDescriptor.gpioPortClearAddress.has_value()
|
|
|| padDescriptor.gpioPortClearAddress == portSetAddress
|
|
) {
|
|
if (padDescriptor.gpioPortClearAddress != portSetAddress) {
|
|
/*
|
|
* We don't need to read the register if the SET and CLEAR operations are performed via different
|
|
* registers.
|
|
*
|
|
* Instead, we can just set the appropriate bit against the SET register.
|
|
*/
|
|
this->writeMemory(
|
|
TargetMemoryType::RAM,
|
|
portSetAddress,
|
|
{static_cast<unsigned char>(0x01 << pinNumber)}
|
|
);
|
|
|
|
} else {
|
|
auto portSetRegisterValue = this->readMemory(TargetMemoryType::RAM, portSetAddress, 1);
|
|
|
|
if (portSetRegisterValue.empty()) {
|
|
throw Exception("Failed to read PORT register value");
|
|
}
|
|
|
|
auto portSetBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(portSetRegisterValue.front());
|
|
if (portSetBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) {
|
|
// PORT set register needs updating
|
|
portSetBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH));
|
|
|
|
this->writeMemory(
|
|
TargetMemoryType::RAM,
|
|
portSetAddress,
|
|
{static_cast<unsigned char>(portSetBitset.to_ulong())}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We only need to update the PORT clear register if the IO state was set to LOW, and the clear register is
|
|
* not the same register as the set register.
|
|
*/
|
|
if (ioState == TargetPinState::IoState::LOW
|
|
&& padDescriptor.gpioPortClearAddress.has_value()
|
|
&& padDescriptor.gpioPortClearAddress != portSetAddress
|
|
) {
|
|
// We also need to ensure the PORT clear register value is correct
|
|
auto portClearAddress = padDescriptor.gpioPortClearAddress.value();
|
|
|
|
this->writeMemory(
|
|
TargetMemoryType::RAM,
|
|
portClearAddress,
|
|
{static_cast<unsigned char>(0x01 << pinNumber)}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Avr8::memoryAddressRangeClashesWithIoPortRegisters(
|
|
TargetMemoryType memoryType,
|
|
std::uint32_t startAddress,
|
|
std::uint32_t endAddress
|
|
) {
|
|
auto& targetParameters = this->targetParameters.value();
|
|
if (targetParameters.mappedIoSegmentStartAddress.has_value() && targetParameters.mappedIoSegmentSize.has_value()) {
|
|
auto mappedIoSegmentStart = targetParameters.mappedIoSegmentStartAddress.value();
|
|
auto mappedIoSegmentEnd = mappedIoSegmentStart + targetParameters.mappedIoSegmentSize.value();
|
|
|
|
return (startAddress >= mappedIoSegmentStart && startAddress <= mappedIoSegmentEnd)
|
|
|| (endAddress >= mappedIoSegmentStart && endAddress <= mappedIoSegmentEnd)
|
|
|| (startAddress <= mappedIoSegmentStart && endAddress >= mappedIoSegmentStart)
|
|
;
|
|
}
|
|
|
|
return false;
|
|
}
|