gigatron/rom/Contrib/at67/graphics.cpp
2025-01-28 19:17:01 +03:00

1214 lines
48 KiB
C++

#include <string>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
#include <atomic>
#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<bool> _enableUploadBar(false);
std::atomic<int> _uploadCursorY(-1);
std::atomic<float> _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; j<height; j++)
{
for(int i=0; i<width; i++)
{
*dstPixels++ = *srcPixels++;
}
}
}
void createFontHeader(const SDL_Surface* fontSurface, const std::string& filename, const std::string& name, int width, int height)
{
std::ofstream outfile(filename);
if(!outfile.is_open())
{
fprintf(stderr, "Graphics::createFontHeader() : failed to create '%s'\n", filename.c_str());
return;
}
outfile << "uint32_t " << name + "[] = \n";
outfile << "{\n";
outfile << " ";
int colCount = 0;
uint32_t* pixels = (uint32_t*)fontSurface->pixels;
for(int j=0; j<height; j++)
{
for(int i=0; i<width; i++)
{
outfile << "0x" << std::hex << std::setw(8) << std::setfill('0') << pixels[j*width + i] << ", ";
if(++colCount == 8)
{
colCount = 0;
if(j < height-1 || i < width-1) outfile << "\n ";
}
}
}
outfile << "\n};" << std::endl;
}
void createHelpTexture(void)
{
bool useDefault = false;
std::ifstream infile(INPUT_CONFIG_INI);
if(!infile.is_open()) useDefault = true;
int lines = 1;
std::string lineToken;
std::vector<std::string> 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<numLines; i++)
{
int y = i*(FONT_HEIGHT + 2) + (maxLines - numLines)/2 * (FONT_HEIGHT + 2);
size_t nonWhiteSpace = lineTokens[i].find_first_not_of(" \n\r\f\t\v");
if(nonWhiteSpace == std::string::npos) lineTokens[i] = std::string(MAX_CHARS_HELP, ' ');
if(lineTokens[i].size() < MAX_CHARS_HELP) lineTokens[i] += std::string(MAX_CHARS_HELP - lineTokens[i].size(), ' ');
drawText(lineTokens[i], pixels, 0, y, 0xFF00FF00, false, 0, 0, 0x00000000, false, -1, true, 0xFFFFFFFF, 0xFF00FFFF);
// Fill in the gaps
for(int j=FONT_HEIGHT; j<FONT_HEIGHT+2; j++)
{
for(int k=0; k<SCREEN_WIDTH*3/4; k++)
{
pixels[(y + j)*SCREEN_WIDTH + k] = 0xFF000000;
}
}
}
// Create help screen texture
_helpTexture = SDL_CreateTextureFromSurface(_renderer, _helpSurface);
if(_helpTexture == NULL)
{
Cpu::shutdown();
fprintf(stderr, "Graphics::createHelpTexture() : failed to create SDL texture.\n");
_EXIT_(EXIT_FAILURE);
}
// Enable blending on help screen texture
SDL_SetTextureBlendMode(_helpTexture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(_helpTexture, 0);
}
bool getKeyAsString(const std::string& sectionString, const std::string& iniKey, const std::string& defaultKey, std::string& result)
{
result = _configIniReader.Get(sectionString, iniKey, defaultKey);
if(result == defaultKey) return false;
Expression::strToUpper(result);
return true;
}
void initialise(void)
{
// HLINE sync error bar
for(int i=0; i<GIGA_HEIGHT; i++) _hlineTiming[i] = 0xFF00FF00;
// Colour palette
for(int i=0; i<COLOUR_PALETTE; i++)
{
uint8_t r = uint8_t(double((i & 0x03) >>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<std::string, Section> 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<GIGA_HEIGHT; i++)
{
Cpu::setRAM(uint16_t(GIGA_VTABLE + i*2), uint8_t((GIGA_VRAM >>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<GIGA_HEIGHT; y++)
{
offsetx += Cpu::getRAM(uint16_t(GIGA_VTABLE + 1 + y * 2));
for (int x = 0; x <= GIGA_WIDTH; x++)
{
uint16_t address = (Cpu::getRAM(uint16_t(GIGA_VTABLE + y * 2)) << 8) + ((offsetx + x) & 0xFF);
uint32_t colour = (x < GIGA_WIDTH) ? _colours[Cpu::getRAM(address) & (COLOUR_PALETTE - 1)] : _hlineTiming[y];
uint32_t screen = (y * 4 % SCREEN_HEIGHT)*SCREEN_WIDTH + (x * 3 % 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 clearScreen(uint32_t colour)
{
for(int y=0; y<SCREEN_HEIGHT; y++)
{
for(int x=0; x<SCREEN_WIDTH*3/4; x++)
{
_pixels[y*SCREEN_WIDTH + x] = colour;
}
}
}
void rectFill(int x0, int y0, int x1, int y1, uint32_t colour)
{
x0 *= 3;
x1 *= 3;
y0 *= 4;
y1 *= 4;
for(int y=y0; y<=y1; y++)
{
for(int x=x0; x<=x1; x++)
{
_pixels[y*SCREEN_WIDTH + x] = colour;
}
}
}
void pixelReticle(const Cpu::State& S, int vgaX, int vgaY)
{
// Draw pixel reticle, but only for active pixels
if(S._PC >= 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<NUM_LEDS; i++)
{
int mask = 1 << (NUM_LEDS-1 - i);
int state = Cpu::getXOUT() & mask;
uint32_t colour = state ? 0xFF00FF00 : 0xFF770000;
int address = int(float(SCREEN_WIDTH) * 0.866f) + i*NUM_LEDS + 3*SCREEN_WIDTH;
_pixels[address + 0] = colour;
_pixels[address + 1] = colour;
_pixels[address + 2] = colour;
address += SCREEN_WIDTH;
_pixels[address + 0] = colour;
_pixels[address + 1] = colour;
_pixels[address + 2] = colour;
}
}
// Simple text routine, font is a non proportional 6*8 font loaded from a 96*48 BMP file
bool drawText(const std::string& text, uint32_t* pixels, int x, int y, uint32_t fgColour, bool invert, int invertSize, int invertPos,
uint32_t bgColour, bool colourKey, int numChars, bool fullscreen, uint32_t commentColour, uint32_t sectionColour)
{
if(!fullscreen)
{
x += UI_START_X;
y += UI_START_Y;
}
if(x<0 || x>=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<numChars; i++)
{
if(sectionColour)
{
static bool useSectionColour = false;
if(x == 0) useSectionColour = false;
if(text.c_str()[i] == '[') useSectionColour = true;
if(text.c_str()[i] == ']') useSectionColour = false;
if(useSectionColour) fgColour = sectionColour;
}
if(commentColour)
{
static bool useCommentColour = false;
if(x == 0) useCommentColour = false;
if(text.c_str()[i] == ';') useCommentColour = true;
if(useCommentColour) fgColour = commentColour;
}
uint8_t chr = text.c_str()[i] - 32;
uint8_t row = chr % CHARS_PER_ROW;
uint8_t col = chr / CHARS_PER_ROW;
int srcx = row*FONT_WIDTH, srcy = col*FONT_HEIGHT;
if(srcx+FONT_WIDTH-1>=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<FONT_WIDTH; j++)
{
for(int k=0; k<FONT_HEIGHT; k++)
{
int fontAddress = (srcx + j) + (srcy + k)*FONT_BMP_WIDTH;
int pixelAddress = (dstx + j) + (dsty + k)*SCREEN_WIDTH;
uint32_t fontPixel = fontPixels[fontAddress] & 0x00FFFFFF;
if((invert && i>=invertPos && i<invertPos+invertSize) ? !fontPixel : fontPixel)
{
pixels[pixelAddress] = 0xFF000000 | fgColour;
}
else
{
if(!colourKey) pixels[pixelAddress] = 0xFF000000 | bgColour;
}
}
}
}
return true;
}
bool drawText(const std::string& text, int x, int y, uint32_t fgColour, bool invert, int invertSize, int invertPos)
{
return drawText(text, _pixels, x, y, fgColour, invert, invertSize, invertPos, 0x00000000, true, -1, true, 0x00000000, 0x00000000);
}
bool drawMenuItem(const std::string& text, int x, int y, uint32_t fgColour, bool invert, int invertSize, uint32_t bgColour)
{
return drawText(text, _pixels, x, y, fgColour, invert, invertSize, 0, bgColour, false, -1, true, 0x00000000, 0x00000000);
}
bool drawDialog(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t bgColour, uint32_t bdColour)
{
Graphics::drawLine(x, y, x+w, y, bdColour);
Graphics::drawLine(x, y+h+1, x+w, y+h+1, bdColour);
Graphics::drawLine(x, y, x, y+h+1, bdColour);
Graphics::drawLine(x+w, y, x+w, y+h+1, bdColour);
for(int16_t yy=y+1; yy<=y+h; yy++)
{
Graphics::drawLine(x+1, yy, x+w-1, yy, bgColour);
}
return true;
}
bool drawDialogItem(const std::string& text, int x, int y, uint32_t fgColour, uint32_t bdColour)
{
return Graphics::drawText(text, _pixels, x, y, fgColour, false, 0, 0, bdColour, false, -1, true, 0x00000000, 0x00000000);
}
void drawDigitBox(uint8_t digit, int x, int y, uint32_t colour)
{
x += UI_START_X;
y += UI_START_Y;
if(x<0 || x>=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<FONT_WIDTH; i++) _pixels[pixelAddress+i] = colour;
}
float powStepRising(float x, float a, float b, float p)
{
float f = std::min(std::max(x, a), b);
f = (f - a) / (b - a);
return powf(f, p);
}
float powStepFalling(float x, float a, float b, float p)
{
float f = std::max(std::min(x, a), b);
f = (f - a) / (b - a);
return powf(f, p);
}
void drawUsageBar(float usage, int x, int y, int w, int h)
{
int ww = int(float(w)*usage);
x += UI_START_X;
y += UI_START_Y;
for(int j=y; j<(y + h); j++)
{
for(int i=x; i<(x + ww); i++)
{
float normalised = float(i - x) / float(w);
uint8_t red = uint8_t(powStepRising(normalised, 0.0f, 0.5f, 1.0f) * 255.0f);
uint8_t grn = uint8_t(powStepFalling(normalised, 1.0f, 0.5f, 1.0f) * 255.0f);
int pixelAddress = i + j*SCREEN_WIDTH;
_pixels[pixelAddress] = (red <<16) + (grn <<8);
}
for(int i=(x + ww); i<(x + w); i++)
{
int pixelAddress = i + j*SCREEN_WIDTH;
_pixels[pixelAddress] = 0x00400000;
}
}
}
void drawUploadBar(float upload)
{
if(!_enableUploadBar || _uploadCursorY == -1) return;
char uploadPercentage[UI_TEXT_SIZE+2] = "";
std::string uploadFilename = _uploadFilename;
if(uploadFilename.size() < UI_TEXT_SIZE+1) uploadFilename.append(UI_TEXT_SIZE+1 - uploadFilename.size(), ' ');
if(upload < 1.0f)
{
strcpy(uploadPercentage, uploadFilename.substr(0, UI_TEXT_SIZE+1).c_str());
sprintf(&uploadPercentage[UI_TEXT_SIZE+1 - 6], " %3d%%\r", int(upload * 100.0f));
}
drawText(uploadPercentage, _pixels, HEX_START_X, int(FONT_CELL_Y*4.4) + _uploadCursorY*FONT_CELL_Y, 0xFFB0B0B0, true, UI_TEXT_SIZE+1, 0, 0x00000000, false, UI_TEXT_SIZE+1);
}
void renderText(void)
{
char str[32];
sprintf(str, "CPU A:%04X B:%04X", 0x200, 0x220);
drawText(std::string(str), _pixels, 0, FONT_CELL_Y*2, 0xFFFFFFFF, false, 0, false);
sprintf(str, "%05.1f%%", Cpu::getvCpuUtilisation() * 100.0);
drawUsageBar(Cpu::getvCpuUtilisation(), FONT_WIDTH*4 - 3, FONT_CELL_Y*2 - 3, FONT_WIDTH*6 + 5, FONT_HEIGHT + 5);
drawText(std::string(str), _pixels, FONT_WIDTH*4, FONT_CELL_Y*2, 0x80808080, false, 0, 0, 0x00000000, true);
//drawText(std::string("LEDS:"), _pixels, 0, 0, 0xFFFFFFFF, false, 0);
sprintf(str, "FPS %5.1f XOUT:%02X IN:%02X", 1.0f / Timing::getFrameTime(), Cpu::getXOUT(), Cpu::getIN());
drawText(std::string(str), _pixels, 0, FONT_CELL_Y, 0xFFFFFFFF, false, 0);
drawText("M: R:", _pixels, 0, 472 - FONT_CELL_Y, 0xFFFFFFFF, false, 0);
switch(Editor::getEditorMode())
{
case Editor::Hex: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Hex "); break;
case Editor::Rom: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Rom "); break;
case Editor::Load: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Load "); break;
case Editor::Dasm: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Dasm "); break;
case Editor::Term: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Term "); break;
case Editor::Image: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Image "); break;
case Editor::Audio: (Editor::getSingleStepEnabled()) ? strcpy(str, "Debug ") : strcpy(str, "Audio "); break;
default: strcpy(str, " ");
}
drawText(std::string(str), _pixels, 12, 472 - FONT_CELL_Y, 0xFF00FF00, false, 0);
switch(Editor::getKeyboardMode())
{
case Editor::Giga: strcpy(str, "Kbd "); break;
case Editor::PS2: strcpy(str, "PS2 "); break;
case Editor::HwGiga: strcpy(str, "HwKbd "); break;
case Editor::HwPS2: strcpy(str, "HwPS2 "); break;
default: strcpy(str, " ");
}
drawText("K:", _pixels, 48, 472 - FONT_CELL_Y, 0xFFFFFFFF, false, 0);
drawText(std::string(str), _pixels, 60, 472 - FONT_CELL_Y, 0xFF00FF00, false, 0);
sprintf(str, "%-5d", Memory::getSizeFreeRAM());
drawText(std::string(str), _pixels, RAM_START, 472 - FONT_CELL_Y, 0xFF00FF00, false, 0);
sprintf(str, " ROM %02X", Cpu::getRomType());
drawText(std::string(VERSION_STR) + std::string(str), _pixels, 0, 472, 0xFFFFFFFF, false, 0);
}
int renderHexMonitor(bool onHex)
{
char str[32] = "";
int hexDigitIndex = -1;
switch(Editor::getMemoryMode())
{
case Editor::RAM: drawText("RAM: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); break;
case Editor::ROM0: drawText("ROM0: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); break;
case Editor::ROM1: drawText("ROM1: Vars:", _pixels, 0, FONT_CELL_Y*3, 0xFFFFFFFF, false, 0); break;
default: break;
}
// 8 * 32 hex display of memory
uint16_t hexAddress = Editor::getHexBaseAddress();
uint16_t cursorAddress = Editor::getHexBaseAddress();
for(int j=0; j<HEX_CHARS_Y; j++)
{
for(int i=0; i<HEX_CHARS_X; i++)
{
uint8_t value = 0;
switch(Editor::getMemoryMode())
{
case Editor::RAM: value = Cpu::getRAM(hexAddress); break;
case Editor::ROM0: value = Cpu::getROM(hexAddress, 0); break;
case Editor::ROM1: value = Cpu::getROM(hexAddress, 1); break;
default: break;
}
sprintf(str, "%02X ", value);
bool onCursor = (i == Editor::getCursorX() && j == Editor::getCursorY());
if(onCursor) hexDigitIndex = j*HEX_CHARS_X + i;
uint32_t colour = (Editor::getHexEdit() && Editor::getMemoryMode() == Editor::RAM && onCursor) ? 0xFF00FF00 : 0xFFB0B0B0;
drawText(std::string(str), _pixels, HEX_START_X + i*HEX_CHAR_WIDE, FONT_CELL_Y*4 + j*(FONT_HEIGHT+FONT_GAP_Y), colour, onCursor, 2);
if(onCursor) cursorAddress = hexAddress;
hexAddress = (Editor::getMemoryMode() == Editor::RAM) ? (hexAddress + 1) & (Memory::getSizeRAM() - 1) : hexAddress + 1;
}
}
sprintf(str, "%04X", cursorAddress);
uint32_t colour = (Editor::getHexEdit() && onHex) ? 0xFF00FF00 : 0xFFFFFFFF;
drawText(std::string(str), _pixels, HEX_START, FONT_CELL_Y*3, colour, onHex, 4);
// Edit digit select for monitor
if(Editor::getHexEdit())
{
// Draw memory digit selection box
if(Editor::getCursorY() >= 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<HEX_CHARS_Y; i++) drawText(" ", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFFFFFFFF, false, 0);
// ROM list
for(int i=0; i<HEX_CHARS_Y; i++)
{
bool onCursor = (i == Editor::getCursorY());
int index = Editor::getRomEntriesIndex() + i;
if(index >= 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<HEX_CHARS_Y; i++) drawText(" ", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFFFFFFFF, false, 0);
// File list
for(int i=0; i<HEX_CHARS_Y; i++)
{
bool onCursor = (i == Editor::getCursorY());
int index = Editor::getFileEntriesIndex() + i;
if(index >= 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<HEX_CHARS_Y; i++) drawText(" ", _pixels, HEX_START_X, FONT_CELL_Y*4 + i*FONT_CELL_Y, 0xFFFFFFFF, false, 0);
for(int i=0; i<Assembler::getDisassembledCodeSize(); i++)
{
bool onPC = false;
if(Editor::getSingleStepEnabled())
{
if(Editor::getMemoryMode() == Editor::RAM)
{
onPC = (Assembler::getDisassembledCode(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<Editor::getVpcBreakPointsSize(); j++)
{
if(Assembler::getDisassembledCode(i)->_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<Editor::getNtvBreakPointsSize(); j++)
{
if(Assembler::getDisassembledCode(i)->_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<HEX_CHARS_X; i++)
{
sprintf(str, "%02X ", Cpu::getRAM(varsAddress++));
drawText(std::string(str), _pixels, HEX_START_X + i*HEX_CHAR_WIDE, int(FONT_CELL_Y*4.25) + FONT_CELL_Y*HEX_CHARS_Y + j*(FONT_HEIGHT+FONT_GAP_Y), 0xFF00FFFF, false, 0);
}
}
// Upload bar
drawUploadBar(_uploadPercentage);
// Page up/down icons
strcpy(str, "^");
drawText(std::string(str), _pixels, PAGEUP_START_X, PAGEUP_START_Y, 0xFF00FF00, Editor::getPageUpButton(), 1);
str[0] = 127; str[1] = 0;
drawText(std::string(str), _pixels, PAGEDN_START_X, PAGEDN_START_Y, 0xFF00FF00, Editor::getPageDnButton(), 1);
// Delete icon, (currently only used for clearing breakpoints)
if(Editor::getEditorMode() == Editor::Dasm && (Editor::getSingleStepEnabled()))
{
int numBrkPoints = (Editor::getMemoryMode() == Editor::RAM) ? Editor::getVpcBreakPointsSize() : Editor::getNtvBreakPointsSize();
sprintf(str, "%02d", numBrkPoints);
drawText(str, _pixels, DELALL_START_X-12, DELALL_START_Y, 0xFFFFFFFF, false, 0);
drawText("x", _pixels, DELALL_START_X, DELALL_START_Y, 0xFFFF0000, Editor::getDelAllButton(), 1);
}
else
{
drawText(" ", _pixels, DELALL_START_X-12, DELALL_START_Y, 0, false, 0);
}
}
void renderHelpScreen(void)
{
// Only display help screen if it is enabled or has alpha > 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();
}
}