components/nvs: batch writes when possible
Introduces new internal function, Page::alterEntryRangeState, which gathers changes to multiple elements of entry state table into a single write, provided that these changes fall into a single word. This allows changing state of up to 16 entries in a single write. Also adds new function, writeEntryData, which writes the whole payload of SZ and BLOB type entries in one go, instead of splitting it into multiple 32-byte writes. This reduces number of writes required for SZ and BLOB entries.
This commit is contained in:
parent
ba75f837b8
commit
076141aab9
4 changed files with 210 additions and 28 deletions
|
@ -109,6 +109,27 @@ esp_err_t Page::writeEntry(const Item& item)
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
|
||||||
|
{
|
||||||
|
assert(size % ENTRY_SIZE == 0);
|
||||||
|
assert(mNextFreeEntry != INVALID_ENTRY);
|
||||||
|
assert(mFirstUsedEntry != INVALID_ENTRY);
|
||||||
|
const uint16_t count = size / ENTRY_SIZE;
|
||||||
|
|
||||||
|
auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast<const uint32_t*>(data), static_cast<uint32_t>(size));
|
||||||
|
if (rc != ESP_OK) {
|
||||||
|
mState = PageState::INVALID;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
auto err = alterEntryRangeState(mNextFreeEntry, mNextFreeEntry + count, EntryState::WRITTEN);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
mUsedEntryCount += count;
|
||||||
|
mNextFreeEntry += count;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize)
|
esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize)
|
||||||
{
|
{
|
||||||
Item item;
|
Item item;
|
||||||
|
@ -170,13 +191,18 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t left = dataSize;
|
size_t left = dataSize / ENTRY_SIZE * ENTRY_SIZE;
|
||||||
while (left != 0) {
|
if (left > 0) {
|
||||||
size_t willWrite = Page::ENTRY_SIZE;
|
err = writeEntryData(static_cast<const uint8_t*>(data), left);
|
||||||
willWrite = (left < willWrite)?left:willWrite;
|
if (err != ESP_OK) {
|
||||||
memcpy(item.rawData, src, willWrite);
|
return err;
|
||||||
src += willWrite;
|
}
|
||||||
left -= willWrite;
|
}
|
||||||
|
|
||||||
|
size_t tail = dataSize - left;
|
||||||
|
if (tail > 0) {
|
||||||
|
std::fill_n(item.rawData, ENTRY_SIZE / 4, 0xffffffff);
|
||||||
|
memcpy(item.rawData, static_cast<const uint8_t*>(data) + left, tail);
|
||||||
err = writeEntry(item);
|
err = writeEntry(item);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
|
@ -290,12 +316,16 @@ esp_err_t Page::eraseEntryAndSpan(size_t index)
|
||||||
if (mEntryTable.get(i) == EntryState::WRITTEN) {
|
if (mEntryTable.get(i) == EntryState::WRITTEN) {
|
||||||
--mUsedEntryCount;
|
--mUsedEntryCount;
|
||||||
}
|
}
|
||||||
rc = alterEntryState(i, EntryState::ERASED);
|
++mErasedEntryCount;
|
||||||
|
}
|
||||||
|
if (span == 1) {
|
||||||
|
rc = alterEntryState(index, EntryState::ERASED);
|
||||||
|
} else {
|
||||||
|
rc = alterEntryRangeState(index, index + span, EntryState::ERASED);
|
||||||
|
}
|
||||||
if (rc != ESP_OK) {
|
if (rc != ESP_OK) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
++mErasedEntryCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -372,17 +402,7 @@ esp_err_t Page::moveItem(Page& other)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = mFirstUsedEntry; i < end; ++i) {
|
return eraseEntryAndSpan(mFirstUsedEntry);
|
||||||
err = eraseEntry(i);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateFirstUsedEntry(mFirstUsedEntry, span);
|
|
||||||
mErasedEntryCount += span;
|
|
||||||
mUsedEntryCount -= span;
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t Page::mLoadEntryTable()
|
esp_err_t Page::mLoadEntryTable()
|
||||||
|
@ -427,7 +447,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||||
// but before the entry state table was altered, the entry locacted via
|
// but before the entry state table was altered, the entry locacted via
|
||||||
// entry state table may actually be half-written.
|
// entry state table may actually be half-written.
|
||||||
// this is easy to check by reading EntryHeader (i.e. first word)
|
// this is easy to check by reading EntryHeader (i.e. first word)
|
||||||
if (mNextFreeEntry != INVALID_ENTRY) {
|
while (mNextFreeEntry < ENTRY_COUNT) {
|
||||||
uint32_t entryAddress = getEntryAddress(mNextFreeEntry);
|
uint32_t entryAddress = getEntryAddress(mNextFreeEntry);
|
||||||
uint32_t header;
|
uint32_t header;
|
||||||
auto rc = spi_flash_read(entryAddress, &header, sizeof(header));
|
auto rc = spi_flash_read(entryAddress, &header, sizeof(header));
|
||||||
|
@ -436,12 +456,20 @@ esp_err_t Page::mLoadEntryTable()
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (header != 0xffffffff) {
|
if (header != 0xffffffff) {
|
||||||
|
auto oldState = mEntryTable.get(mNextFreeEntry);
|
||||||
auto err = alterEntryState(mNextFreeEntry, EntryState::ERASED);
|
auto err = alterEntryState(mNextFreeEntry, EntryState::ERASED);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
mState = PageState::INVALID;
|
mState = PageState::INVALID;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
++mNextFreeEntry;
|
++mNextFreeEntry;
|
||||||
|
if (oldState == EntryState::WRITTEN) {
|
||||||
|
--mUsedEntryCount;
|
||||||
|
}
|
||||||
|
++mErasedEntryCount;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,6 +601,31 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state)
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
|
||||||
|
{
|
||||||
|
assert(end <= ENTRY_COUNT);
|
||||||
|
assert(end > begin);
|
||||||
|
size_t wordIndex = mEntryTable.getWordIndex(end - 1);
|
||||||
|
for (ptrdiff_t i = end - 1; i >= static_cast<ptrdiff_t>(begin); --i) {
|
||||||
|
mEntryTable.set(i, state);
|
||||||
|
size_t nextWordIndex;
|
||||||
|
if (i == static_cast<ptrdiff_t>(begin)) {
|
||||||
|
nextWordIndex = (size_t) -1;
|
||||||
|
} else {
|
||||||
|
nextWordIndex = mEntryTable.getWordIndex(i - 1);
|
||||||
|
}
|
||||||
|
if (nextWordIndex != wordIndex) {
|
||||||
|
uint32_t word = mEntryTable.data()[wordIndex];
|
||||||
|
auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4, &word, 4);
|
||||||
|
if (rc != ESP_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wordIndex = nextWordIndex;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t Page::alterPageState(PageState state)
|
esp_err_t Page::alterPageState(PageState state)
|
||||||
{
|
{
|
||||||
auto rc = spi_flash_write(mBaseAddress, reinterpret_cast<uint32_t*>(&state), sizeof(state));
|
auto rc = spi_flash_write(mBaseAddress, reinterpret_cast<uint32_t*>(&state), sizeof(state));
|
||||||
|
|
|
@ -194,12 +194,16 @@ protected:
|
||||||
|
|
||||||
esp_err_t alterEntryState(size_t index, EntryState state);
|
esp_err_t alterEntryState(size_t index, EntryState state);
|
||||||
|
|
||||||
|
esp_err_t alterEntryRangeState(size_t begin, size_t end, EntryState state);
|
||||||
|
|
||||||
esp_err_t alterPageState(PageState state);
|
esp_err_t alterPageState(PageState state);
|
||||||
|
|
||||||
esp_err_t readEntry(size_t index, Item& dst) const;
|
esp_err_t readEntry(size_t index, Item& dst) const;
|
||||||
|
|
||||||
esp_err_t writeEntry(const Item& item);
|
esp_err_t writeEntry(const Item& item);
|
||||||
|
|
||||||
|
esp_err_t writeEntryData(const uint8_t* data, size_t size);
|
||||||
|
|
||||||
esp_err_t eraseEntry(size_t index);
|
esp_err_t eraseEntry(size_t index);
|
||||||
|
|
||||||
esp_err_t eraseEntryAndSpan(size_t index);
|
esp_err_t eraseEntryAndSpan(size_t index);
|
||||||
|
|
|
@ -234,6 +234,7 @@ void Storage::debugCheck()
|
||||||
|
|
||||||
for (auto p = mPageManager.begin(); p != mPageManager.end(); ++p) {
|
for (auto p = mPageManager.begin(); p != mPageManager.end(); ++p) {
|
||||||
size_t itemIndex = 0;
|
size_t itemIndex = 0;
|
||||||
|
size_t usedCount = 0;
|
||||||
Item item;
|
Item item;
|
||||||
while (p->findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) {
|
while (p->findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) {
|
||||||
std::stringstream keyrepr;
|
std::stringstream keyrepr;
|
||||||
|
@ -246,7 +247,9 @@ void Storage::debugCheck()
|
||||||
}
|
}
|
||||||
keys.insert(std::make_pair(keystr, static_cast<Page*>(p)));
|
keys.insert(std::make_pair(keystr, static_cast<Page*>(p)));
|
||||||
itemIndex += item.span;
|
itemIndex += item.span;
|
||||||
|
usedCount += item.span;
|
||||||
}
|
}
|
||||||
|
assert(usedCount == p->getUsedEntryCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif //ESP_PLATFORM
|
#endif //ESP_PLATFORM
|
||||||
|
|
|
@ -443,18 +443,140 @@ TEST_CASE("wifi test", "[nvs]")
|
||||||
SpiFlashEmulator emu(10);
|
SpiFlashEmulator emu(10);
|
||||||
emu.randomize(10);
|
emu.randomize(10);
|
||||||
|
|
||||||
nvs_handle handle;
|
|
||||||
const uint32_t NVS_FLASH_SECTOR = 5;
|
const uint32_t NVS_FLASH_SECTOR = 5;
|
||||||
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
|
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
|
||||||
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
|
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
|
||||||
TEST_ESP_OK(nvs_flash_init(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN));
|
TEST_ESP_OK(nvs_flash_init(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN));
|
||||||
|
|
||||||
TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &handle));
|
nvs_handle misc_handle;
|
||||||
|
TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &misc_handle));
|
||||||
|
char log[33];
|
||||||
|
size_t log_size = sizeof(log);
|
||||||
|
TEST_ESP_ERR(nvs_get_str(misc_handle, "log", log, &log_size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
strcpy(log, "foobarbazfizzz");
|
||||||
|
TEST_ESP_OK(nvs_set_str(misc_handle, "log", log));
|
||||||
|
|
||||||
|
nvs_handle net80211_handle;
|
||||||
|
TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &net80211_handle));
|
||||||
|
|
||||||
uint8_t opmode = 2;
|
uint8_t opmode = 2;
|
||||||
if (nvs_get_u8(handle, "wifi.opmode", &opmode) != ESP_OK) {
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.opmode", &opmode), ESP_ERR_NVS_NOT_FOUND);
|
||||||
TEST_ESP_OK(nvs_set_u8(handle, "wifi.opmode", opmode));
|
|
||||||
}
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.opmode", opmode));
|
||||||
|
|
||||||
|
uint8_t country = 0;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.country", &opmode), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.country", opmode));
|
||||||
|
|
||||||
|
char ssid[36];
|
||||||
|
size_t size = sizeof(ssid);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
strcpy(ssid, "my android AP");
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.ssid", ssid, size));
|
||||||
|
|
||||||
|
char mac[6];
|
||||||
|
size = sizeof(mac);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(mac, 0xab, 6);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.mac", mac, size));
|
||||||
|
|
||||||
|
uint8_t authmode = 1;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.authmode", authmode));
|
||||||
|
|
||||||
|
char pswd[65];
|
||||||
|
size = sizeof(pswd);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pswd", pswd, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
strcpy(pswd, "`123456788990-=");
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pswd", pswd, size));
|
||||||
|
|
||||||
|
char pmk[32];
|
||||||
|
size = sizeof(pmk);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(pmk, 1, size);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pmk", pmk, size));
|
||||||
|
|
||||||
|
uint8_t chan = 1;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.chan", &chan), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.chan", chan));
|
||||||
|
|
||||||
|
uint8_t autoconn = 1;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "auto.conn", &autoconn), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "auto.conn", autoconn));
|
||||||
|
|
||||||
|
uint8_t bssid_set = 1;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bssid.set", &bssid_set), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "bssid.set", bssid_set));
|
||||||
|
|
||||||
|
char bssid[6];
|
||||||
|
size = sizeof(bssid);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.bssid", bssid, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(mac, 0xcd, 6);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.bssid", bssid, size));
|
||||||
|
|
||||||
|
uint8_t phym = 3;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phym", &phym), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phym", phym));
|
||||||
|
|
||||||
|
uint8_t phybw = 2;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phybw", &phybw), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phybw", phybw));
|
||||||
|
|
||||||
|
char apsw[2];
|
||||||
|
size = sizeof(apsw);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apsw", apsw, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(apsw, 0x2, size);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apsw", apsw, size));
|
||||||
|
|
||||||
|
char apinfo[700];
|
||||||
|
size = sizeof(apinfo);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apinfo", apinfo, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(apinfo, 0, size);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apinfo", apinfo, size));
|
||||||
|
|
||||||
|
size = sizeof(ssid);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
strcpy(ssid, "ESP_A2F340");
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.ssid", ssid, size));
|
||||||
|
|
||||||
|
size = sizeof(mac);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(mac, 0xac, 6);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.mac", mac, size));
|
||||||
|
|
||||||
|
size = sizeof(pswd);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.passwd", pswd, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
strcpy(pswd, "");
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.passwd", pswd, size));
|
||||||
|
|
||||||
|
size = sizeof(pmk);
|
||||||
|
TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
memset(pmk, 1, size);
|
||||||
|
TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.pmk", pmk, size));
|
||||||
|
|
||||||
|
chan = 6;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.chan", &chan), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.chan", chan));
|
||||||
|
|
||||||
|
authmode = 0;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.authmode", authmode));
|
||||||
|
|
||||||
|
uint8_t hidden = 0;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.hidden", &hidden), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.hidden", hidden));
|
||||||
|
|
||||||
|
uint8_t max_conn = 4;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.max.conn", &max_conn), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.max.conn", max_conn));
|
||||||
|
|
||||||
|
uint8_t bcn_interval = 2;
|
||||||
|
TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bcn_interval", &bcn_interval), ESP_ERR_NVS_NOT_FOUND);
|
||||||
|
TEST_ESP_OK(nvs_set_u8(net80211_handle, "bcn_interval", bcn_interval));
|
||||||
|
|
||||||
|
s_perf << "Time to simulate nvs init with wifi libs: " << emu.getTotalTime() << " us (" << emu.getEraseOps() << "E " << emu.getWriteOps() << "W " << emu.getReadOps() << "R " << emu.getWriteBytes() << "Wb " << emu.getReadBytes() << "Rb)" << std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue