#include #include #include #include #include #include #include "memory.h" #include "timing.h" #include "editor.h" #include "loader.h" #include "graphics.h" #include "expression.h" #include "assembler.h" #include "inih/INIReader.h" #include "defaultKeys.h" // Use this if you ever want to change the default font, but it better be 6x8 per char or otherwise you will be in a world of hurt //#define CREATE_FONT_HEADER #ifndef CREATE_FONT_HEADER #include "emuFont96x48.h" #endif namespace Graphics { int _width, _height; int _posX = 0, _posY = 0; float _aspect = 0.0f; float _scaleX, _scaleY; bool _fullScreen = false; bool _resizable = false; bool _borderless = true; bool _vSync = false; bool _fixedSize = false; int _filter = 0; bool _displayHelpScreen = false; uint8_t _displayHelpScreenAlpha = 0; std::atomic _enableUploadBar(false); std::atomic _uploadCursorY(-1); std::atomic _uploadPercentage(0.0f); std::string _uploadFilename; uint32_t _pixels[SCREEN_WIDTH * SCREEN_HEIGHT]; uint32_t _colours[COLOUR_PALETTE]; uint32_t _hlineTiming[GIGA_HEIGHT]; SDL_Window* _window = NULL; SDL_Renderer* _renderer = NULL; SDL_Texture* _screenTexture = NULL; SDL_Texture* _helpTexture = NULL; SDL_Surface* _helpSurface = NULL; SDL_Surface* _fontSurface = NULL; INIReader _configIniReader; int getWidth(void) {return _width;} int getHeight(void) {return _height;} uint32_t* getPixels(void) {return _pixels;} uint32_t* getColours(void) {return _colours;} SDL_Window* getWindow(void) {return _window;} SDL_Renderer* getRenderer(void) {return _renderer;} SDL_Texture* getScreenTexture(void) {return _screenTexture;} SDL_Texture* getHelpTexture(void) {return _helpTexture;} SDL_Surface* getHelpSurface(void) {return _helpSurface;} SDL_Surface* getFontSurface(void) {return _fontSurface;} void setDisplayHelpScreen(bool display) {_displayHelpScreen = display;} void setWidthHeight(int width, int height) {_width = width, _height = height;} bool getUploadBarEnabled(void) {return _enableUploadBar;} void setUploadFilename(const std::string& uploadFilename) {_uploadFilename = uploadFilename;} void updateUploadBar(float uploadPercentage) {_uploadPercentage = uploadPercentage;} void enableUploadBar(bool enableUploadBar) { _uploadPercentage = 0.0f; _enableUploadBar = enableUploadBar; if(enableUploadBar) { _uploadCursorY = 34; } else { _uploadCursorY = -1; _uploadFilename = ""; } } SDL_Surface* createSurface(int width, int height) { uint32_t rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32, rmask, gmask, bmask, amask); if(surface == NULL) { Cpu::shutdown(); fprintf(stderr, "Graphics::createSurface() : failed to create SDL surface.\n"); _EXIT_(EXIT_FAILURE); } return surface; } void writeToSurface(const SDL_Surface* surface, const uint32_t* data, int width, int height) { uint32_t* srcPixels = (uint32_t*)data; uint32_t* dstPixels = (uint32_t*)surface->pixels; for(int j=0; jpixels; for(int j=0; j lineTokens; if(useDefault) { lineTokens = _defaultKeys; lines = int(_defaultKeys.size()); } else { // Read ini file into lines buffer while(!infile.eof()) { std::getline(infile, lineToken); lineTokens.push_back(lineToken); if(!infile.good() && !infile.eof()) { Cpu::shutdown(); fprintf(stderr, "Graphics::createHelpTexture() : Bad line : '%s' : in %s : on line %d\n", lineToken.c_str(), INPUT_CONFIG_INI, lines); _EXIT_(EXIT_FAILURE); } lines++; } } // Print text in lines with spaces as padding to the help screen surface int maxLines = SCREEN_HEIGHT / (FONT_HEIGHT + 2); int numLines = std::min(lines-1, maxLines); uint32_t* pixels = (uint32_t*)_helpSurface->pixels; for(int i=0; i>0) / 3.0 * 255.0); uint8_t g = uint8_t(double((i & 0x0C) >>2) / 3.0 * 255.0); uint8_t b = uint8_t(double((i & 0x30) >>4) / 3.0 * 255.0); _colours[i] = 0xFF000000 | (r <<16) | (g <<8) | b; //fprintf(stderr, "%08X\n", _colours[i]); // use to create a Paint.Net palette } // Safe resolution by default SDL_DisplayMode DM; SDL_GetCurrentDisplayMode(0, &DM); _aspect = float(DM.w) / float(DM.h); _fullScreen = false; _resizable = true; _borderless = false; _vSync = false; _fixedSize = false; _filter = 0; _width = 640; _height = 480; _scaleX = 1.5f; _scaleY = 1.5f; _posX = 40; _posY = 40; // Parse graphics config file INIReader iniReader(Loader::getExePath() + "/" + GRAPHICS_CONFIG_INI); _configIniReader = iniReader; if(_configIniReader.ParseError() < 0) { fprintf(stderr, "Graphics::initialise() : couldn't load INI file '%s' : reverting to default graphics options.\n", GRAPHICS_CONFIG_INI); } else { // Parse Monitor Keys enum Section {Monitor}; std::map section; section["Monitor"] = Monitor; for(auto sectionString : _configIniReader.Sections()) { if(section.find(sectionString) == section.end()) { fprintf(stderr, "Graphics::initialise() : INI file '%s' has bad Sections : reverting to default values.\n", GRAPHICS_CONFIG_INI); break; } std::string result; switch(section[sectionString]) { case Monitor: { getKeyAsString(sectionString, "Fullscreen", "0", result); _fullScreen = strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "Resizable", "1", result); _resizable = strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "Borderless", "0", result); _borderless = strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "VSync", "0", result); _vSync = strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "FixedSize", "0", result); _fixedSize = strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "Filter", "0", result); _filter = strtol(result.c_str(), nullptr, 10); _filter = (_filter<0 || _filter>2) ? 0 : _filter; getKeyAsString(sectionString, "Width", "640", result); _width = (result == "DESKTOP") ? DM.w : std::strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "Height", "480", result); _height = (result == "DESKTOP") ? DM.h : std::strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "ScaleX", "1.5", result); _scaleX = std::stof(result.c_str()); getKeyAsString(sectionString, "ScaleY", "1.5", result); _scaleY = std::stof(result.c_str()); getKeyAsString(sectionString, "PosX", "0", result); _posX = strtol(result.c_str(), nullptr, 10); getKeyAsString(sectionString, "PosY", "0", result); _posY = strtol(result.c_str(), nullptr, 10); } break; default: break; } } } // SDL hints, VSync and Batching char vsChar = char(_vSync + '0'); SDL_SetHint(SDL_HINT_RENDER_VSYNC, &vsChar); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, std::to_string(_filter).c_str()); // Fullscreen if(_fullScreen) { if(SDL_CreateWindowAndRenderer(0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP, &_window, &_renderer) < 0) { Cpu::shutdown(); fprintf(stderr, "Graphics::initialise() : failed to create SDL fullscreen window.\n"); _EXIT_(EXIT_FAILURE); } } // Windowed else { if(_fixedSize) { SDL_RenderSetLogicalSize(_renderer, _width, _height); if(SDL_CreateWindowAndRenderer(_width, _height, 0, &_window, &_renderer) < 0) { Cpu::shutdown(); fprintf(stderr, "Graphics::initialise() : failed to create SDL window.\n"); _EXIT_(EXIT_FAILURE); } SDL_SetWindowTitle(_window, VERSION_STR); } else { // Try and keep faithful to Gigatron's real aspect ratio no matter what the window/monitor resolution is _width = int(_width * _scaleX * _aspect * float(_height) / float(_width)); _height = int(_height * _scaleY); if(SDL_CreateWindowAndRenderer(_width, _height, 0, &_window, &_renderer) < 0) { Cpu::shutdown(); fprintf(stderr, "Graphics::initialise() : failed to create SDL window.\n"); _EXIT_(EXIT_FAILURE); } if(!_borderless) { SDL_SetWindowResizable(_window, (SDL_bool)_resizable); SDL_SetWindowTitle(_window, VERSION_STR); } SDL_SetWindowBordered(_window, (SDL_bool)!_borderless); SDL_SetWindowPosition(_window, _posX, _posY); } } // Screen texture _screenTexture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); if(_screenTexture == NULL) { Cpu::shutdown(); fprintf(stderr, "Graphics::initialise() : failed to create SDL texture.\n"); _EXIT_(EXIT_FAILURE); } #ifdef CREATE_FONT_HEADER // Load font file SDL_Surface* fontSurface = SDL_LoadBMP("EmuFont-96x48.bmp"); if(fontSurface == NULL) { Cpu::shutdown(); fprintf(stderr, "Graphics::initialise() : failed to create SDL font surface, you're probably missing 'EmuFont-96x48.bmp' in the current directory/path.\n"); _EXIT_(EXIT_FAILURE); } _fontSurface = SDL_ConvertSurfaceFormat(fontSurface, SDL_PIXELFORMAT_ARGB8888, NULL); SDL_FreeSurface(fontSurface); if(_fontSurface == NULL) { Cpu::shutdown(); fprintf(stderr, "Graphics::initialise() : failed to convert SDL font surface format to screen surface format.\n"); _EXIT_(EXIT_FAILURE); } // Use this if you want to change the default font createFontHeader(_fontSurface, "emuFont96x48.h", "_emuFont96x48", FONT_BMP_WIDTH, FONT_BMP_HEIGHT); #else _fontSurface = createSurface(FONT_BMP_WIDTH, FONT_BMP_HEIGHT); writeToSurface(_fontSurface, _emuFont96x48, FONT_BMP_WIDTH, FONT_BMP_HEIGHT); #endif // Help screen _helpSurface = createSurface(SCREEN_WIDTH, SCREEN_HEIGHT); createHelpTexture(); #ifdef _WIN32 Cpu::restoreWin32Console(); #endif SDL_RaiseWindow(_window); } uint32_t savedPixels[6*5]; void restoreReticlePixel(int x, int y) { if(x < 0 || x > SCREEN_WIDTH - 1) return; if(y < 0 || y > SCREEN_HEIGHT - 1) return; int index = (x % 5) + (y % 6)*5; _pixels[x + y*SCREEN_WIDTH] = savedPixels[index]; } void saveReticlePixel(int x, int y) { if(x < 0 || x > SCREEN_WIDTH - 1) return; if(y < 0 || y > SCREEN_HEIGHT - 1) return; int index = (x % 5) + (y % 6)*5; savedPixels[index] = _pixels[x + y*SCREEN_WIDTH]; } void drawReticlePixel(int x, int y) { if(x < 0 || x > SCREEN_WIDTH - 1) return; if(y < 0 || y > SCREEN_HEIGHT - 1) return; //fprintf(stderr, "%d %d\n", x, y); _pixels[x + y*SCREEN_WIDTH] = 0xFFFFFFFF; //(0x00FFFFFF ^ _pixels[x + y*SCREEN_WIDTH]) & 0x00FFFFFF; } void drawReticle(int vgaX, int vgaY) { for(int j=-1; j<5; j++) { for(int i=-1; i<4; i++) { int x = vgaX*3 + i; int y = vgaY*4 + j; saveReticlePixel(x, y); if(j==-1 || j==4 || i==-1 || i==3) drawReticlePixel(x, y); } } } void restoreReticle(int vgaX, int vgaY) { for(int j=-1; j<5; j++) { for(int i=-1; i<4; i++) { int x = vgaX*3 + i; int y = vgaY*4 + j; restoreReticlePixel(x, y); } } } void resetVTable(void) { for(int i=0; i>8) + i)); Cpu::setRAM(uint16_t(GIGA_VTABLE + 1 + i*2), 0x00); } } void refreshTimingPixel(const Cpu::State& S, int vgaX, int pixelY, uint32_t colour, bool debugging) { UNREFERENCED_PARAM(S); _hlineTiming[pixelY % GIGA_HEIGHT] = colour; if(debugging) return; uint32_t screen = (vgaX % (GIGA_WIDTH + 1))*3 + (pixelY % GIGA_HEIGHT)*4*SCREEN_WIDTH; _pixels[screen + 0 + 0*SCREEN_WIDTH] = colour; _pixels[screen + 1 + 0*SCREEN_WIDTH] = colour; _pixels[screen + 2 + 0*SCREEN_WIDTH] = colour; _pixels[screen + 0 + 1*SCREEN_WIDTH] = colour; _pixels[screen + 1 + 1*SCREEN_WIDTH] = colour; _pixels[screen + 2 + 1*SCREEN_WIDTH] = colour; _pixels[screen + 0 + 2*SCREEN_WIDTH] = colour; _pixels[screen + 1 + 2*SCREEN_WIDTH] = colour; _pixels[screen + 2 + 2*SCREEN_WIDTH] = colour; _pixels[screen + 0 + 3*SCREEN_WIDTH] = 0x00; _pixels[screen + 1 + 3*SCREEN_WIDTH] = 0x00; _pixels[screen + 2 + 3*SCREEN_WIDTH] = 0x00; } void refreshPixel(const Cpu::State& S, int vgaX, int vgaY) { uint32_t colour = _colours[S._OUT & (COLOUR_PALETTE - 1)]; uint32_t address = (vgaX % GIGA_WIDTH)*3 + (vgaY % SCREEN_HEIGHT)*SCREEN_WIDTH; _pixels[address + 0] = colour; _pixels[address + 1] = colour; _pixels[address + 2] = colour; } void refreshScreen(void) { uint8_t offsetx = 0; for (int y = 0; y= 0x020D && S._PC <= 0x02AD) { if(vgaX > 0) restoreReticle(vgaX - 1, vgaY/4); if(vgaX < HPIXELS_END - HPIXELS_START) drawReticle(vgaX, vgaY/4); } } void drawPixel(uint8_t x, uint8_t y, uint32_t colour) { x = x % GIGA_WIDTH; y = y % GIGA_HEIGHT; uint32_t screen = x*3 + y*4*SCREEN_WIDTH; _pixels[screen + 0 + 0*SCREEN_WIDTH] = colour; _pixels[screen + 1 + 0*SCREEN_WIDTH] = colour; _pixels[screen + 2 + 0*SCREEN_WIDTH] = colour; _pixels[screen + 0 + 1*SCREEN_WIDTH] = colour; _pixels[screen + 1 + 1*SCREEN_WIDTH] = colour; _pixels[screen + 2 + 1*SCREEN_WIDTH] = colour; _pixels[screen + 0 + 2*SCREEN_WIDTH] = colour; _pixels[screen + 1 + 2*SCREEN_WIDTH] = colour; _pixels[screen + 2 + 2*SCREEN_WIDTH] = colour; _pixels[screen + 0 + 3*SCREEN_WIDTH] = 0x00; _pixels[screen + 1 + 3*SCREEN_WIDTH] = 0x00; _pixels[screen + 2 + 3*SCREEN_WIDTH] = 0x00; } void drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t colour) { int16_t sx = x2 - x1; int16_t sy = y2 - y1; int h = sy; int16_t dx1 = 1, dx2 = 1, dy1 = 1, dy2 = 0; if(sx & 0x8000) { dx1 = -1; dx2 = -1; sx = 0 - sx; } if(sy & 0x8000) { dy1 = -1; sy = 0 - sy; if(sx < sy) dy2 = -1; } if(sx < sy) { dx2 = 0; std::swap(sx, sy); if(h > 0) dy2 = 1; } int16_t numerator = sx >> 1; int16_t xy1 = x1 | (y1<<8); int16_t xy2 = x2 | (y2<<8); int16_t dxy1 = dx1 + (dy1<<8); int16_t dxy2 = dx2 + (dy2<<8); for(uint16_t i=0; i<=sx/2; i++) { drawPixel(uint8_t(xy1), uint8_t(xy1>>8), colour); drawPixel(uint8_t(xy2), uint8_t(xy2>>8), colour); numerator += sy; if(numerator > sx) { numerator -= sx; xy1 += dxy1; xy2 -= dxy1; } else { xy1 += dxy2; xy2 -= dxy2; } } } void drawLeds(void) { for(int i=0; i=SCREEN_WIDTH || y<0 || y>=SCREEN_HEIGHT) return false; uint32_t* fontPixels = (uint32_t*)_fontSurface->pixels; numChars = (numChars == -1) ? int(text.size()) : numChars; for(int i=0; i=FONT_BMP_WIDTH || srcy+FONT_HEIGHT-1>=FONT_BMP_HEIGHT) return false; int dstx = x + i*FONT_WIDTH, dsty = y; if(dstx+FONT_WIDTH-1>=SCREEN_WIDTH-FONT_WIDTH || dsty+FONT_HEIGHT-1>=SCREEN_HEIGHT) return false; for(int j=0; j=invertPos && i=SCREEN_WIDTH || y<0 || y>=SCREEN_HEIGHT) return; uint32_t pixelAddress = x + digit*FONT_WIDTH + y*SCREEN_WIDTH; pixelAddress += (FONT_HEIGHT-1)*SCREEN_WIDTH; for(int i=0; i= 0 && Editor::getCursorY() < 32 && Editor::getCursorX() < 8 && Editor::getMemoryMode() == Editor::RAM) { drawDigitBox(Editor::getMemoryDigit(), HEX_START_X + Editor::getCursorX()*HEX_CHAR_WIDE, FONT_CELL_Y*4 + Editor::getCursorY()*FONT_CELL_Y, 0xFFFF00FF); } } return hexDigitIndex; } void renderRomBrowser(void) { drawText("ROM: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); // Clear window for(int i=0; i= int(Editor::getRomEntriesSize())) break; uint32_t colour = (i < NUM_INT_ROMS) ? 0xFFB0B0B0 : 0xFFFFFFFF; if(i == Cpu::getRomIndex()) drawText("*", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFFD0D000, onCursor, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); drawText(*Editor::getRomEntryName(index), _pixels, HEX_START_X + 6, FONT_CELL_Y*4 + i*FONT_CELL_Y, colour, onCursor, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); } // ROM type char str[32] = ""; (Editor::getCursorY() < 0 || Editor::getCursorY() >= Editor::getRomEntriesSize()) ? sprintf(str, " ") : sprintf(str, "%02X", Editor::getRomEntryType(Editor::getCursorY())); drawText(std::string(str), _pixels, HEX_START-6, FONT_CELL_Y*3, 0xFFFFFFFF, false, 4); } void renderLoadBrowser(bool onHex) { uint16_t hexLoadAddress = (Editor::getEditorMode() == Editor::Load) ? Editor::getLoadBaseAddress() : Editor::getHexBaseAddress(); drawText("Load: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); // Clear window for(int i=0; i= int(Editor::getFileEntriesSize())) break; uint32_t colour = (Editor::getFileEntryType(index) == Editor::Dir) ? 0xFFB0B0B0 : 0xFFFFFFFF; drawText(*Editor::getFileEntryName(index), _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, colour, onCursor, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); } // Load address char str[32] = ""; sprintf(str, "%04X", hexLoadAddress); uint32_t colour = (Editor::getHexEdit() && onHex) ? 0xFF00FF00 : 0xFFFFFFFF; drawText(std::string(str), _pixels, HEX_START, FONT_CELL_Y*3, colour, onHex, 4); } void renderDisassembler(bool onHex) { char str[32] = ""; (Editor::getMemoryMode() == Editor::RAM) ? Assembler::disassemble(Editor::getVpcBaseAddress()) : Assembler::disassemble(Editor::getNtvBaseAddress()); //sprintf(str, "%d\n", Editor::getVpcBreakpointsSize()); //sprintf(str, "%d\n", Editor::getNtvBreakpointsSize()); //fprintf(stderr, str); // Clear window for(int i=0; i_address == Cpu::getVPC()); } else { onPC = (Assembler::getDisassembledCode(i)->_address == Cpu::getStateS()._PC); } } // Program counter icon in debug mode bool onCursor = (i == Editor::getCursorY()); if(onPC) drawText(">", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFF00FF00, onCursor, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); // Breakpoint icons if(Editor::getMemoryMode() == Editor::RAM) { for(int j=0; j_address == Editor::getVpcBreakPointAddress(j) && Editor::getSingleStepEnabled()) { drawText("*", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFFC000C0, onCursor, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); break; } } } else { for(int j=0; j_address == Editor::getNtvBreakPointAddress(j) && Editor::getSingleStepEnabled()) { drawText("*", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFFC0C000, onCursor, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); break; } } } // Mnemonic, highlight if on vPC and show cursor in debug mode uint32_t colour = (onPC) ? 0xFFFFFFFF : 0xFFB0B0B0; bool highlight = (onCursor || onPC) && (Editor::getSingleStepEnabled()); drawText(Assembler::getDisassembledCode(i)->_text, _pixels, HEX_START_X+6, FONT_CELL_Y*4 + i*FONT_CELL_Y, colour, highlight, UI_TEXT_SIZE, 0, 0x00000000, false, UI_TEXT_SIZE); } switch(Editor::getMemoryMode()) { case Editor::RAM: drawText("RAM: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); break; case Editor::ROM0: drawText("ROM: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); break; case Editor::ROM1: drawText("ROM: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); break; default: break; } (Editor::getMemoryMode() == Editor::RAM) ? sprintf(str, "%04X", Editor::getVpcBaseAddress()) : sprintf(str, "%04X", Editor::getNtvBaseAddress()); uint32_t colour = (Editor::getHexEdit() && onHex) ? 0xFF00FF00 : 0xFFFFFFFF; drawText(std::string(str), _pixels, HEX_START, FONT_CELL_Y*3, colour, onHex, 4); if(Editor::getMemoryMode() == Editor::RAM) { // Display vCPU registers sprintf(str, "PC:%04X LR:%04X Fn:%04X", Cpu::getVPC(), Cpu::getRAM(0x001A) | (Cpu::getRAM(0x001B)<<8), Cpu::getRAM(0x0022) | (Cpu::getRAM(0x0023)<<8)); drawText(std::string(str), _pixels, HEX_START_X, int(FONT_CELL_Y*2.0) + FONT_CELL_Y*HEX_CHARS_Y, 0xFF00FFFF, false, 0); sprintf(str, "AC:%04X SP:%04X Sr:%04X", Cpu::getRAM(0x0018) | (Cpu::getRAM(0x0019)<<8), Assembler::getvSpMin() | (Cpu::getRAM(0x001C)<<8), Cpu::getRAM(0x000F) | (Cpu::getRAM(0x0011)<<8)); drawText(std::string(str), _pixels, HEX_START_X, int(FONT_CELL_Y*3.0) + FONT_CELL_Y*HEX_CHARS_Y, 0xFF00FFFF, false, 0); } else { // Display Native registers sprintf(str, "PC:%04X IR:%02X OUT:%02X", Cpu::getStateS()._PC, Cpu::getStateT()._IR, Cpu::getStateT()._OUT); drawText(std::string(str), _pixels, HEX_START_X, int(FONT_CELL_Y*2.0) + FONT_CELL_Y*HEX_CHARS_Y, 0xFF00FFFF, false, 0); sprintf(str, "AC:%02X X:%02X Y:%02X D:%02X", Cpu::getStateT()._AC, Cpu::getStateT()._X, Cpu::getStateT()._Y, Cpu::getStateT()._D); drawText(std::string(str), _pixels, HEX_START_X, int(FONT_CELL_Y*3.0) + FONT_CELL_Y*HEX_CHARS_Y, 0xFF00FFFF, false, 0); } } void renderTextWindow(void) { char str[32] = ""; int x, y, cy; Editor::getMouseUiCursor(x, y, cy); Editor::setCursorX(x); Editor::setCursorY(cy); // Addresses uint16_t cpuUsageAddressA = Editor::getCpuUsageAddressA(); uint16_t cpuUsageAddressB = Editor::getCpuUsageAddressB(); uint16_t varsAddress = Editor::getVarsBaseAddress(); // Mouse cursor over vars Editor::OnVarType onVarType = Editor::getOnVarType(); bool onHex = (onVarType == Editor::OnHex); bool onCpuA = (onVarType == Editor::OnCpuA); bool onCpuB = (onVarType == Editor::OnCpuB); bool onVars = (onVarType == Editor::OnVars); bool onWatch = (onVarType == Editor::OnWatch); uint8_t onVarMask = uint8_t(onHex) | (uint8_t(onCpuA) <<1) | (uint8_t(onCpuB) <<2) | (uint8_t(onVars) <<3) | (uint8_t(onWatch) <<4); static uint8_t onVarMaskPrev = onVarMask; // Text window int hexDigitIndex = -1; static int hexDigitIndexPrev = hexDigitIndex; switch(Editor::getEditorMode()) { case Editor::Hex: hexDigitIndex = renderHexMonitor(onHex); break; case Editor::Rom: renderRomBrowser(); break; case Editor::Audio: case Editor::Image: case Editor::Load: renderLoadBrowser(onHex); break; case Editor::Dasm: renderDisassembler(onHex); break; default: break; } // Disable hex/var editing when mouse moves off a currently editing field if(onVarMask != onVarMaskPrev || hexDigitIndex != hexDigitIndexPrev) Editor::setHexEdit(false); onVarMaskPrev = onVarMask; hexDigitIndexPrev = hexDigitIndex; // Draw addresses if(Editor::getSingleStepEnabled()) { drawText("Watch:", _pixels, WATCH_START, FONT_CELL_Y*2, 0xFFFFFFFF, false, 0); sprintf(str, "%04X ", Editor::getSingleStepAddress()); uint32_t colour = (Editor::getHexEdit() && onWatch) ? 0xFF00FF00 : 0xFFFFFFFF; drawText(str, _pixels, WATCH_START+36, FONT_CELL_Y*2, colour, onWatch, 4); } else { sprintf(str, "%04X", cpuUsageAddressA); uint32_t colourA = (Editor::getHexEdit() && onCpuA) ? 0xFF00FF00 : 0xFFFFFFFF; drawText(std::string(str), _pixels, CPUA_START, FONT_CELL_Y*2, colourA, onCpuA, 4); sprintf(str, "%04X", cpuUsageAddressB); uint32_t colourB = (Editor::getHexEdit() && onCpuB) ? 0xFF00FF00 : 0xFFFFFFFF; drawText(std::string(str), _pixels, CPUB_START, FONT_CELL_Y*2, colourB, onCpuB, 4); } sprintf(str, "%04X", varsAddress); uint32_t colour = (Editor::getHexEdit() && onVars) ? 0xFF00FF00 : 0xFFFFFFFF; drawText(std::string(str), _pixels, VAR_START, FONT_CELL_Y*3, colour, onVars, 4); // Edit digit select for addresses if(Editor::getHexEdit()) { // Draw address digit selections if(onCpuA) drawDigitBox(Editor::getAddressDigit(), CPUA_START, FONT_CELL_Y*2, 0xFFFF00FF); else if(onCpuB) drawDigitBox(Editor::getAddressDigit(), CPUB_START, FONT_CELL_Y*2, 0xFFFF00FF); else if(onHex) drawDigitBox(Editor::getAddressDigit(), HEX_START, FONT_CELL_Y*3, 0xFFFF00FF); else if(onVars) drawDigitBox(Editor::getAddressDigit(), VAR_START, FONT_CELL_Y*3, 0xFFFF00FF); else if(onWatch) drawDigitBox(Editor::getAddressDigit(), WATCH_START+36, FONT_CELL_Y*2, 0xFFFF00FF); } // 8 * 2 hex display of vCPU program variables for(int j=0; j<2; j++) { for(int i=0; i 0 if(_displayHelpScreen || _displayHelpScreenAlpha) { SDL_SetTextureAlphaMod(_helpTexture, _displayHelpScreenAlpha); SDL_RenderCopy(_renderer, _helpTexture, NULL, NULL); // Fade help screen in if(_displayHelpScreen && _displayHelpScreenAlpha < 220) { _displayHelpScreenAlpha += 10; if(_displayHelpScreenAlpha > 220) _displayHelpScreenAlpha = 220; } // Fade help screen out if(!_displayHelpScreen && _displayHelpScreenAlpha > 0) { _displayHelpScreenAlpha -= 10; if(_displayHelpScreenAlpha > 240) _displayHelpScreenAlpha = 0; } } } void render(bool synchronise) { drawLeds(); renderText(); renderTextWindow(); SDL_UpdateTexture(_screenTexture, NULL, _pixels, SCREEN_WIDTH * sizeof(uint32_t)); SDL_RenderCopy(_renderer, _screenTexture, NULL, NULL); renderHelpScreen(); SDL_RenderPresent(_renderer); if(synchronise) Timing::synchronise(); } }