From 450a317cd74bb2573b88042e1c059c1bbff5f359 Mon Sep 17 00:00:00 2001 From: meepingsnesroms Date: Mon, 23 Apr 2018 17:30:15 -0700 Subject: [PATCH] Framebuffer now drawing 1/8 of the boot logo on the left side For some reason Palm uses the picture in picture registers for the bootlogo. HexViewer can now dump memory to .bin files. --- qtBuildSystem/Mu/Mu.pro | 6 +- qtBuildSystem/Mu/fileaccess.cpp | 90 +++++++++++++++++++++++++++++ qtBuildSystem/Mu/fileaccess.h | 19 +++++++ qtBuildSystem/Mu/hexviewer.cpp | 28 ++++++++- qtBuildSystem/Mu/hexviewer.h | 2 + qtBuildSystem/Mu/hexviewer.ui | 38 ++++++++++++- qtBuildSystem/Mu/mainwindow.cpp | 55 +----------------- qtBuildSystem/Mu/mainwindow.h | 11 ---- src/emulator.c | 4 -- src/hardwareRegisters.c | 3 + src/sed1376.c | 98 ++++++++++++++++++++++++++++---- src/sed1376Accessors.c.h | 2 +- tests/screenMem.bin | Bin 0 -> 81920 bytes tests/screenMem2.bin | Bin 0 -> 81920 bytes 14 files changed, 271 insertions(+), 85 deletions(-) create mode 100644 qtBuildSystem/Mu/fileaccess.cpp create mode 100644 qtBuildSystem/Mu/fileaccess.h create mode 100644 tests/screenMem.bin create mode 100644 tests/screenMem2.bin diff --git a/qtBuildSystem/Mu/Mu.pro b/qtBuildSystem/Mu/Mu.pro index 69fb671..1663f3c 100644 --- a/qtBuildSystem/Mu/Mu.pro +++ b/qtBuildSystem/Mu/Mu.pro @@ -48,7 +48,8 @@ SOURCES += \ src/sed1376.c \ src/silkscreen.c \ src/trapNumToName.c \ - hexviewer.cpp + hexviewer.cpp \ + fileaccess.cpp HEADERS += \ qt-common/include/boolean.h \ @@ -77,7 +78,8 @@ HEADERS += \ hexviewer.h \ src/hardwareRegistersTiming.c.h \ src/sed1376Accessors.c.h \ - src/hardwareRegistersAccessors.c.h + src/hardwareRegistersAccessors.c.h \ + fileaccess.h FORMS += \ mainwindow.ui \ diff --git a/qtBuildSystem/Mu/fileaccess.cpp b/qtBuildSystem/Mu/fileaccess.cpp new file mode 100644 index 0000000..4815cfc --- /dev/null +++ b/qtBuildSystem/Mu/fileaccess.cpp @@ -0,0 +1,90 @@ +#include + +#include +#include +#include +#include +#include + +#include "fileaccess.h" + +uint8_t* getFileBuffer(std::string filePath, size_t& size, uint32_t& error){ + uint8_t* rawData = NULL; + if(filePath != ""){ + struct stat st; + int doesntExist = stat(filePath.c_str(), &st); + if(doesntExist == 0 && st.st_size){ + FILE* dataFile = fopen(filePath.c_str(), "rb"); + if(dataFile != NULL){ + rawData = new (std::nothrow) uint8_t[st.st_size]; + if(rawData){ + size_t bytesRead = fread(rawData, 1, st.st_size, dataFile); + if(bytesRead == (size_t)st.st_size){ + size = bytesRead; + error = FRONTEND_ERR_NONE; + } + else{ + error = FRONTEND_FILE_UNFINISHED; + } + } + else{ + error = FRONTEND_OUT_OF_MEMORY; + } + fclose(dataFile); + } + else{ + error = FRONTEND_FILE_PROTECTED; + } + } + else{ + error = FRONTEND_FILE_DOESNT_EXIST; + } + } + else{ + error = FRONTEND_FILE_EMPTY_PATH; + } + + if(error != FRONTEND_ERR_NONE){ + if(rawData) + delete[] rawData; + rawData = NULL; + size = 0; + } + + return rawData; +} + +uint32_t setFileBuffer(std::string filePath, uint8_t* data, size_t size){ + uint32_t error = FRONTEND_ERR_NONE; + if(filePath != ""){ + FILE* dataFile = fopen(filePath.c_str(), "wb"); + if(dataFile != NULL){ + size_t bytesWritten = fwrite(data, 1, size, dataFile); + if(bytesWritten != size) + error = FRONTEND_FILE_UNFINISHED; + fclose(dataFile); + } + else{ + error = FRONTEND_FILE_PROTECTED; + } + } + else{ + error = FRONTEND_FILE_EMPTY_PATH; + } + return error; +} + +bool validFilePath(std::string path){ +#ifdef Q_OS_WIN + if(path.length() < 3 || !isalpha(path[0]) || path[1] != ':' || (path[2] != '/' && path[2] != '\\')) + return false; +#else + if(path.length() < 1 || path[0] != '/') + return false; +#endif + for(uint32_t count = 0; count < path.length(); count++){ + if(!isalnum(path[count]) && !ispunct(path[count])) + return false; + } + return true; +} diff --git a/qtBuildSystem/Mu/fileaccess.h b/qtBuildSystem/Mu/fileaccess.h new file mode 100644 index 0000000..34abf01 --- /dev/null +++ b/qtBuildSystem/Mu/fileaccess.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +enum{ + FRONTEND_ERR_NONE, + FRONTEND_FILE_DOESNT_EXIST, + FRONTEND_FILE_EMPTY_PATH, + FRONTEND_FILE_UNFINISHED, + FRONTEND_FILE_MODIFYED, + FRONTEND_FILE_INVALID_TYPE, + FRONTEND_FILE_PROTECTED, + FRONTEND_OUT_OF_MEMORY +}; + +uint8_t* getFileBuffer(std::string filePath, size_t& size, uint32_t& error); +uint32_t setFileBuffer(std::string filePath, uint8_t* data, size_t size); +bool validFilePath(std::string path); diff --git a/qtBuildSystem/Mu/hexviewer.cpp b/qtBuildSystem/Mu/hexviewer.cpp index 4d628e9..f1bd18f 100644 --- a/qtBuildSystem/Mu/hexviewer.cpp +++ b/qtBuildSystem/Mu/hexviewer.cpp @@ -3,6 +3,8 @@ #include +#include "fileaccess.h" + extern "C" { #include "src/m68k/m68k.h" #include "src/hardwareRegisters.h" @@ -13,7 +15,7 @@ extern "C" { #define INVALID_NUMBER (int64_t)0x8000000000000000//the biggest negative 64bit number -uint32_t getEmulatorMemorySafe(uint32_t address, uint8_t size){ +int64_t getEmulatorMemorySafe(uint32_t address, uint8_t size){ //until SPI and UART destructive reads are implemented all reads to mapped addresses are safe if(bankType[START_BANK(address)] != CHIP_NONE){ uint16_t m68kSr = m68k_get_reg(NULL, M68K_REG_SR); @@ -31,7 +33,7 @@ uint32_t getEmulatorMemorySafe(uint32_t address, uint8_t size){ } m68k_set_reg(M68K_REG_SR, m68kSr); } - return 0x00000000; + return INVALID_NUMBER; } @@ -116,10 +118,14 @@ void HexViewer::on_hexUpdate_clicked(){ if(address != INVALID_NUMBER && length != INVALID_NUMBER && length != 0 && address + bits / 8 * length - 1 <= 0xFFFFFFFF){ for(int64_t count = 0; count < length; count++){ + int64_t data = getEmulatorMemorySafe(address, bits); QString value; value += stringFromNumber(address, true, 8); value += ":"; - value += stringFromNumber(getEmulatorMemorySafe(address, bits), true, bits / 8 * 2); + if(data != INVALID_NUMBER) + value += stringFromNumber(data, true, bits / 8 * 2); + else + value += "Unsafe Access"; ui->hexValueList->addItem(value); address += bits / 8; } @@ -143,3 +149,19 @@ void HexViewer::on_hex32Bit_clicked(){ bitsPerEntry = 32; hexRadioButtonHandler(); } + +void HexViewer::on_hexDump_clicked() +{ + int64_t address = numberFromString(ui->hexAddress->text(), false/*negative allowed*/); + int64_t length = numberFromString(ui->hexLength->text(), false/*negative allowed*/); + uint8_t bits = bitsPerEntry; + std::string filePath = ui->hexFilePath->text().toStdString(); + if(validFilePath(filePath) && address != INVALID_NUMBER && length != INVALID_NUMBER && length != 0 && address + bits / 8 * length - 1 <= 0xFFFFFFFF){ + length *= bits / 8; + uint8_t* dumpBuffer = new uint8_t[length]; + for(int64_t count = 0; count < length; count++) + dumpBuffer[count] = getEmulatorMemorySafe(address + count, 8); + setFileBuffer(filePath, dumpBuffer, length); + delete[] dumpBuffer; + } +} diff --git a/qtBuildSystem/Mu/hexviewer.h b/qtBuildSystem/Mu/hexviewer.h index 0111c29..29e7d5b 100644 --- a/qtBuildSystem/Mu/hexviewer.h +++ b/qtBuildSystem/Mu/hexviewer.h @@ -29,6 +29,8 @@ private slots: void on_hex32Bit_clicked(); + void on_hexDump_clicked(); + private: uint8_t bitsPerEntry; Ui::HexViewer *ui; diff --git a/qtBuildSystem/Mu/hexviewer.ui b/qtBuildSystem/Mu/hexviewer.ui index 5a1eb20..98d91f6 100644 --- a/qtBuildSystem/Mu/hexviewer.ui +++ b/qtBuildSystem/Mu/hexviewer.ui @@ -174,6 +174,42 @@ + + + + 272 + 180 + 161 + 32 + + + + Dump + + + + + + 272 + 160 + 161 + 21 + + + + + + + 270 + 140 + 60 + 16 + + + + File Path + + @@ -295,7 +331,7 @@ - + 110 diff --git a/qtBuildSystem/Mu/mainwindow.cpp b/qtBuildSystem/Mu/mainwindow.cpp index 334be48..b77cf0e 100644 --- a/qtBuildSystem/Mu/mainwindow.cpp +++ b/qtBuildSystem/Mu/mainwindow.cpp @@ -10,17 +10,15 @@ #include #include -#include #include #include -#include -#include -#include +#include #include "hexviewer.h" - +#include "fileaccess.h" #include "src/emulator.h" + uint32_t screenWidth; uint32_t screenHeight; @@ -34,53 +32,6 @@ static std::atomic emuInited; static uint8_t romBuffer[ROM_SIZE]; -uint8_t* getFileBuffer(std::string filePath, size_t& size, uint32_t& error){ - uint8_t* rawData = NULL; - if(filePath != ""){ - struct stat st; - int doesntExist = stat(filePath.c_str(), &st); - if(doesntExist == 0 && st.st_size){ - FILE* dataFile = fopen(filePath.c_str(), "rb"); - if(dataFile != NULL){ - rawData = new (std::nothrow) uint8_t[st.st_size]; - if(rawData){ - size_t bytesRead = fread(rawData, 1, st.st_size, dataFile); - if(bytesRead == (size_t)st.st_size){ - size = bytesRead; - error = FRONTEND_ERR_NONE; - } - else{ - error = FRONTEND_FILE_UNFINISHED; - } - } - else{ - error = FRONTEND_OUT_OF_MEMORY; - } - fclose(dataFile); - } - else{ - error = FRONTEND_FILE_PROTECTED; - } - } - else{ - error = FRONTEND_FILE_DOESNT_EXIST; - } - } - else{ - error = FRONTEND_FILE_EMPTY_PATH; - } - - if(error != FRONTEND_ERR_NONE){ - if(rawData) - delete[] rawData; - rawData = NULL; - size = 0; - } - - return rawData; -} - - MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow){ diff --git a/qtBuildSystem/Mu/mainwindow.h b/qtBuildSystem/Mu/mainwindow.h index 309cb27..c8861e6 100644 --- a/qtBuildSystem/Mu/mainwindow.h +++ b/qtBuildSystem/Mu/mainwindow.h @@ -7,17 +7,6 @@ extern uint32_t screenWidth; extern uint32_t screenHeight; -enum{ - FRONTEND_ERR_NONE, - FRONTEND_FILE_DOESNT_EXIST, - FRONTEND_FILE_EMPTY_PATH, - FRONTEND_FILE_UNFINISHED, - FRONTEND_FILE_MODIFYED, - FRONTEND_FILE_INVALID_TYPE, - FRONTEND_FILE_PROTECTED, - FRONTEND_OUT_OF_MEMORY -}; - namespace Ui { class MainWindow; } diff --git a/src/emulator.c b/src/emulator.c index b280c91..e1b5eb1 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -530,10 +530,6 @@ void emulateFrame(){ palmCycleCounter -= CPU_FREQUENCY / EMU_FPS; sed1376Render(); - - //memcpy(palmFramebuffer, sed1376Framebuffer, 160 * 160 * sizeof(uint16_t)); - - //debugLog("Ran frame, executed %f cycles.\n", palmCycleCounter + CPU_FREQUENCY / EMU_FPS); } bool emulateUntilDebugEventOrFrameEnd(){ diff --git a/src/hardwareRegisters.c b/src/hardwareRegisters.c index 796e80c..3ad1dc3 100644 --- a/src/hardwareRegisters.c +++ b/src/hardwareRegisters.c @@ -629,11 +629,13 @@ void setHwRegister16(unsigned int address, unsigned int value){ //this is a 32 bit register but Palm OS writes it as 16 bit chunks registerArrayWrite16(IPR, registerArrayRead16(IPR) & ~(value & 0x001F/*external hardware int mask*/)); registerArrayWrite16(ISR, registerArrayRead16(ISR) & ~(value & 0x001F/*external hardware int mask*/)); + checkInterrupts(); break; case ISR + 2: //this is a 32 bit register but Palm OS writes it as 16 bit chunks registerArrayWrite16(IPR + 2, registerArrayRead16(IPR + 2) & ~(value & 0x0F00/*external hardware int mask*/)); registerArrayWrite16(ISR + 2, registerArrayRead16(ISR + 2) & ~(value & 0x0F00/*external hardware int mask*/)); + checkInterrupts(); break; case TCTL1: @@ -652,6 +654,7 @@ void setHwRegister16(unsigned int address, unsigned int value){ clearIprIsrBit(INT_RTI); if(!(registerArrayRead16(RTCISR) & 0x003F)) clearIprIsrBit(INT_RTC); + checkInterrupts(); break; case PLLFSR: diff --git a/src/sed1376.c b/src/sed1376.c index f783771..2868183 100644 --- a/src/sed1376.c +++ b/src/sed1376.c @@ -70,6 +70,39 @@ static inline uint32_t getBufferStartAddress(){ return screenStartAddress; } +static inline uint32_t getPipStartAddress(){ + uint32_t pipStartAddress = sed1376Registers[PIP_ADDR_2] << 16 | sed1376Registers[PIP_ADDR_1] << 8 | sed1376Registers[PIP_ADDR_0]; + switch((sed1376Registers[SPECIAL_EFFECT] & 0x03) * 90){ + + case 0: + //desired byte address / 4. + pipStartAddress *= 4; + break; + + case 90: + //((desired byte address + (panel height * bpp / 8)) / 4) - 1. + pipStartAddress += 1; + pipStartAddress *= 4; + //pipStartAddress - (panelHeight * bpp / 8); + break; + + case 180: + //((desired byte address + (panel width * panel height * bpp / 8)) / 4) - 1. + pipStartAddress += 1; + pipStartAddress *= 4; + //pipStartAddress - (panelWidth * panelHeight * bpp / 8); + break; + + case 270: + //(desired byte address + ((panel width - 1) * panel height * bpp / 8)) / 4. + pipStartAddress *= 4; + //pipStartAddress -= ((panelWidth - 1) * panelHeight * bpp / 8); + break; + } + + return pipStartAddress; +} + static inline void updateLcdStatus(){ palmMisc.lcdOn = CAST_TO_BOOL(sed1376Registers[GPIO_CONT_0] & sed1376Registers[GPIO_CONF_0] & 0x20); palmMisc.backlightOn = CAST_TO_BOOL(sed1376Registers[GPIO_CONT_0] & sed1376Registers[GPIO_CONF_0] & 0x10); @@ -87,10 +120,19 @@ uint8_t sed1376GetRegister(uint8_t address){ #endif switch(address){ - case PWR_SAVE_CFG://may need to attach "Vertical Non- Display Period Status" to RNG - debugLog("Read SED1376 power save config, PC 0x%08X.\n", m68k_get_reg(NULL, M68K_REG_PC)); - return sed1376Registers[address] | 0x80;//pretend where in a non dispaly period + case LUT_READ_LOC: + case LUT_WRITE_LOC: + case LUT_R_WRITE: + case LUT_G_WRITE: + case LUT_B_WRITE: + //write only + return 0x00; + case PWR_SAVE_CFG: + case SPECIAL_EFFECT: + case PIP_ADDR_0: + case PIP_ADDR_1: + case PIP_ADDR_2: case SCRATCH_0: case SCRATCH_1: case GPIO_CONF_0: @@ -116,22 +158,33 @@ void sed1376SetRegister(uint8_t address, uint8_t value){ switch(address){ case PWR_SAVE_CFG: - sed1376Registers[address] = value & 0x8E; + //bit 7 must always be set, timing hack + sed1376Registers[address] = (value & 0x01) | 0x80; break; case DISP_MODE: sed1376Registers[address] = value & 0xF7; break; + case PANEL_TYPE: + sed1376Registers[address] = value & 0xFB; + break; + case SPECIAL_EFFECT: sed1376Registers[address] = value & 0xD3; break; case DISP_ADDR_2: + case PIP_ADDR_2: sed1376Registers[address] = value & 0x01; break; case LINE_SIZE_1: + case PIP_LINE_SZ_1: + case PIP_X_START_1: + case PIP_X_END_1: + case PIP_Y_START_1: + case PIP_Y_END_1: sed1376Registers[address] = value & 0x03; break; @@ -139,7 +192,10 @@ void sed1376SetRegister(uint8_t address, uint8_t value){ sed1376RLut[value] = sed1376Registers[LUT_R_WRITE]; sed1376GLut[value] = sed1376Registers[LUT_G_WRITE]; sed1376BLut[value] = sed1376Registers[LUT_B_WRITE]; - sed1376OutputLut[value] = makeRgb16FromRgb666(sed1376Registers[LUT_R_WRITE], sed1376Registers[LUT_G_WRITE], sed1376Registers[LUT_B_WRITE]); + sed1376Registers[LUT_R_READ] = sed1376RLut[value]; + sed1376Registers[LUT_G_READ] = sed1376GLut[value]; + sed1376Registers[LUT_B_READ] = sed1376BLut[value]; + sed1376OutputLut[value] = makeRgb16FromRgb666(sed1376RLut[value], sed1376GLut[value], sed1376BLut[value]); break; case LUT_READ_LOC: @@ -171,7 +227,14 @@ void sed1376SetRegister(uint8_t address, uint8_t value){ case SCRATCH_1: case DISP_ADDR_0: case DISP_ADDR_1: + case PIP_ADDR_0: + case PIP_ADDR_1: case LINE_SIZE_0: + case PIP_LINE_SZ_0: + case PIP_X_START_0: + case PIP_X_END_0: + case PIP_Y_START_0: + case PIP_Y_END_0: case LUT_R_WRITE: case LUT_G_WRITE: case LUT_B_WRITE: @@ -202,6 +265,9 @@ void sed1376Reset(){ sed1376Registers[REV_CODE] = 0x28; sed1376Registers[DISP_BUFF_SIZE] = 0x14; + + //timing hack + sed1376Registers[PWR_SAVE_CFG] = 0x80; } void sed1376RefreshLut(){ @@ -226,15 +292,25 @@ void sed1376Render(){ for(uint16_t pixelX = 0; pixelX < 160; pixelX++) palmFramebuffer[pixelY * 160 + pixelX] = renderPixel(pixelX, pixelY); - debugLog("Screen start address:0x%08X, buffer width:%d, swivel view:%d degrees\n", screenStartAddress, lineSize, rotation); + //debugLog("Screen start address:0x%08X, buffer width:%d, swivel view:%d degrees\n", screenStartAddress, lineSize, rotation); + //debugLog("Screen format, monochrome:%s, BPP:%d\n", monochrome ? "true" : "false", bitDepth); - /* if(pictureInPictureEnabled){ - screenStartAddress = getBufferStartAddress(); - lineSize = (sed1376Registers[PIP_LINE_SZ_1] << 8 | sed1376Registers[PIP_LINE_SZ_0]) * 4; - //not done yet + uint16_t pipStartX = sed1376Registers[PIP_X_START_1] << 8 | sed1376Registers[PIP_X_START_0]; + uint16_t pipStartY = sed1376Registers[PIP_Y_START_1] << 8 | sed1376Registers[PIP_Y_START_0]; + uint16_t pipEndX = sed1376Registers[PIP_X_END_1] << 8 | sed1376Registers[PIP_X_END_0]; + uint16_t pipEndY = sed1376Registers[PIP_Y_END_1] << 8 | sed1376Registers[PIP_Y_END_0]; + debugLog("PIP state, start x:%d, end x:%d, start y:%d, end y:%d\n", pipStartX, pipEndX, pipStartY, pipEndY); + if(pipStartX < 160 && pipStartY < 160){ + pipEndX = pipEndX < 160 ? pipEndX : 160; + pipEndY = pipEndY < 160 ? pipEndY : 160; + screenStartAddress = getPipStartAddress(); + lineSize = (sed1376Registers[PIP_LINE_SZ_1] << 8 | sed1376Registers[PIP_LINE_SZ_0]) * 4; + for(uint16_t pixelY = pipStartY; pixelY < pipEndY; pixelY++) + for(uint16_t pixelX = pipStartX; pixelX < pipEndX; pixelX++) + palmFramebuffer[pixelY * 160 + pixelX] = renderPixel(pixelX, pixelY); + } } - */ //rotation //later diff --git a/src/sed1376Accessors.c.h b/src/sed1376Accessors.c.h index 0e6b340..f43385c 100644 --- a/src/sed1376Accessors.c.h +++ b/src/sed1376Accessors.c.h @@ -13,7 +13,7 @@ static inline uint32_t handlePanelDataSwaps(uint32_t address){ static inline uint16_t makeRgb16FromRgb666(uint8_t r, uint8_t g, uint8_t b){ uint16_t color = r << 10 & 0xF800; color |= g << 5 & 0x07E0; - color |= b >> 1 & 0x008F; + color |= b >> 1 & 0x001F; return color; } static inline void makeRgb666FromRgb16(uint16_t color, uint8_t* r, uint8_t* g, uint8_t* b){ diff --git a/tests/screenMem.bin b/tests/screenMem.bin new file mode 100644 index 0000000000000000000000000000000000000000..ada42ecce4c479f8f7ad1e12602e4327a8b8be7f GIT binary patch literal 81920 zcmZP=1*0J_8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF z0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O zqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF z0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O zqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF z0;3@?8UmvsFd71*U^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ON zU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q nLtr!nMnhmU1V%#u8Ulx+Yr8By`Ei1{EB-V72hpRLejxw=gd_${ literal 0 HcmV?d00001 diff --git a/tests/screenMem2.bin b/tests/screenMem2.bin new file mode 100644 index 0000000000000000000000000000000000000000..4bc35a9e41c1137ffd5092c1b92578786898c11f GIT binary patch literal 81920 zcmZP=1*0J_8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd70h4}k}gl9Knp zKvMGl1Dab$ngtIejqcoDy6?vI|NpPwxOx4?zMHph-nw~n-_3pd_U*fI~v&%{%vK;;{$!?%ck$@2=$i`}gmI0@3K^(mO_XN%J8{_5HhdZr-{FVo?>}zkBD_ z&HE3a;J)O&dw1{Nx^?I7eR6EMf9EEZT_|~H>8<+=4<0;_yk~dn;)OeR?@3DDy=%1e z_8m!b9LRA0_8n@*vgDmxcON`>aR08ItKF&XH!fbhc=3YKy?b|!?kqJT&w&hgmy#9q zSVKed&MnCY4<6iewbRkj(Q!Qqa^J-Zr|wYX!23(76@vHg+`9MR!F_u>oqPB0>DX=G z0e0WT8~bkEy=!E&)QHT0xPOZ}4t#KT>D>npB*R^FB=6nR(RST&3gW(t7fLTue+Fb@Hwmq8te z?%AKW|zb00W<|AW)tMTQ5USiX4i0)ymrP(u(Ae&8Vdj}&;g-G`JG$#dkL z|MwYA?Yjta$^&rX13M8E*B}PCpm}irKPdceLDC;Q{J`!5H>N=W2hJF9l%-ESxPKkwKJ@VW&j1NR1_r46K(2x$K@baqDR!UH4aoojm_%m6N!LGcT690S9JeWxTJ zNP=7WpuQZqe7?Z|Dxj}l2Zb6a*+JqL!UB;eIKA)Gz6TGC?t&WmH_2#?P~O72Z*=pX>1f}*>-vD<7D>$)H z0I4Ju-zHZhibW6Z-o69sjDuRvU^iYkwPSm@osP~ukn`B@-X%BqC~sUr+!}OdU3DHjV0iFAa_L<%L+}3W+Z6YVK<fAUBflEF_%|?%%s-bm!K->;JD`-*@xY?WIeX-nw<`*3FwY zK|RYG@Db?$*YONNBaJ`bWnch>AeF)olc3FJ#;{;j1;y>em5Iu_N7Xko%Y41k> literal 0 HcmV?d00001