From c73d051e54072cdef97ae70157f32e6556b1b852 Mon Sep 17 00:00:00 2001 From: Frank van den Hoef Date: Sat, 21 Oct 2023 17:10:30 +0200 Subject: [PATCH] 80-columns mode support --- System/emulator/Common.h | 2 +- System/emulator/Config.h | 2 +- System/emulator/EmuState.cpp | 23 +++++-- System/emulator/EmuState.h | 4 +- System/emulator/UI/UI.cpp | 19 +++--- System/emulator/Video.cpp | 117 ++++++++++++++++++----------------- System/emulator/Video.h | 14 +++++ 7 files changed, 104 insertions(+), 77 deletions(-) diff --git a/System/emulator/Common.h b/System/emulator/Common.h index 21edaee8..e59bf329 100644 --- a/System/emulator/Common.h +++ b/System/emulator/Common.h @@ -50,7 +50,7 @@ #define CPU_FREQ (3579545) -#define VIDEO_WIDTH (352) +#define VIDEO_WIDTH (704) #define VIDEO_HEIGHT (240) static inline void stripTrailingSlashes(std::string &path) { diff --git a/System/emulator/Config.h b/System/emulator/Config.h index d6588a36..63de6d0b 100644 --- a/System/emulator/Config.h +++ b/System/emulator/Config.h @@ -23,7 +23,7 @@ class Config { int wndPosX = SDL_WINDOWPOS_CENTERED; int wndPosY = SDL_WINDOWPOS_CENTERED; int wndWidth = VIDEO_WIDTH * 2; - int wndHeight = VIDEO_HEIGHT * 2; + int wndHeight = (VIDEO_HEIGHT * 2) * 2; int scrScale = 1; bool enableSound = true; bool enableMouse = true; diff --git a/System/emulator/EmuState.cpp b/System/emulator/EmuState.cpp index b0e2256f..825dedea 100644 --- a/System/emulator/EmuState.cpp +++ b/System/emulator/EmuState.cpp @@ -340,12 +340,25 @@ void EmuState::memWrite(size_t param, uint16_t addr, uint8_t data) { addr &= 0x3FFF; if (overlayRam && addr >= 0x3000) { - if (addr < 0x3400) { - emuState.screenRam[addr & 0x3FF] = data; - } else if (addr < 0x3800) { - emuState.colorRam[addr & 0x3FF] = data; - } else { + if (addr >= 0x3800) { emuState.mainRam[addr] = data; + return; + } + + if (emuState.videoCtrl & VCTRL_80_COLUMNS) { + if (emuState.videoCtrl & VCTRL_TRAM_PAGE) { + emuState.colorRam[addr & 0x7FF] = data; + } else { + emuState.screenRam[addr & 0x7FF] = data; + } + + } else { + unsigned offset = (emuState.videoCtrl & VCTRL_TRAM_PAGE) ? 0x400 : 0; + if (addr < 0x3400) { + emuState.screenRam[offset | (addr & 0x3FF)] = data; + } else { + emuState.colorRam[offset | (addr & 0x3FF)] = data; + } } return; } diff --git a/System/emulator/EmuState.h b/System/emulator/EmuState.h index 67afd0bc..0fa23a0f 100644 --- a/System/emulator/EmuState.h +++ b/System/emulator/EmuState.h @@ -136,8 +136,8 @@ struct EmuState { bool cpmRemap = false; // $FD<1>: Remap memory for CP/M // Memory space - uint8_t screenRam[1024]; // $3000-33FF: Screen RAM for text mode - uint8_t colorRam[1024]; // $3400-37FF: Color RAM for text mode + uint8_t screenRam[2048]; // $3000-33FF: Screen RAM for text mode + uint8_t colorRam[2048]; // $3400-37FF: Color RAM for text mode uint8_t systemRom[256 * 1024]; // Flash memory uint8_t mainRam[512 * 1024]; // Main RAM uint8_t cartRom[16 * 1024]; // Cartridge ROM diff --git a/System/emulator/UI/UI.cpp b/System/emulator/UI/UI.cpp index e56308e9..c2575230 100644 --- a/System/emulator/UI/UI.cpp +++ b/System/emulator/UI/UI.cpp @@ -60,7 +60,7 @@ void UI::start( } // Create screen texture - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, VIDEO_WIDTH, VIDEO_HEIGHT); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, VIDEO_WIDTH, VIDEO_HEIGHT * 2); if (texture == NULL) { fprintf(stderr, "SDL_CreateTexture Error: %s\n", SDL_GetError()); SDL_Quit(); @@ -337,7 +337,7 @@ void UI::mainLoop() { // Update mouse const ImVec2 p0((float)dst.x, (float)dst.y); const ImVec2 p1((float)(dst.x + dst.w), (float)(dst.y + dst.h)); - auto pos = (io.MousePos - p0) / (p1 - p0) * ImVec2(VIDEO_WIDTH, VIDEO_HEIGHT) - ImVec2(16, 16); + auto pos = (io.MousePos - p0) / (p1 - p0) * ImVec2(VIDEO_WIDTH / 2, VIDEO_HEIGHT) - ImVec2(16, 16); bool hideMouse = (emuState.mouseHideTimeout > 0) && @@ -377,11 +377,10 @@ void UI::renderScreen() { const uint16_t *fb = emuState.video.getFb(); - for (int j = 0; j < VIDEO_HEIGHT; j++) { + for (int j = 0; j < VIDEO_HEIGHT * 2; j++) { for (int i = 0; i < VIDEO_WIDTH; i++) { - // Convert from RGB444 to RGB888 - uint16_t col444 = fb[j * VIDEO_WIDTH + i]; + uint16_t col444 = fb[j / 2 * VIDEO_WIDTH + i]; unsigned r4 = (col444 >> 8) & 0xF; unsigned g4 = (col444 >> 4) & 0xF; @@ -417,9 +416,9 @@ SDL_Rect UI::renderTexture() { // Retain aspect ratio int w1 = (w / VIDEO_WIDTH) * VIDEO_WIDTH; - int h1 = (w1 * VIDEO_HEIGHT) / VIDEO_WIDTH; - int h2 = (h / VIDEO_HEIGHT) * VIDEO_HEIGHT; - int w2 = (h2 * VIDEO_WIDTH) / VIDEO_HEIGHT; + int h1 = (w1 * (VIDEO_HEIGHT * 2)) / VIDEO_WIDTH; + int h2 = (h / (VIDEO_HEIGHT * 2)) * (VIDEO_HEIGHT * 2); + int w2 = (h2 * VIDEO_WIDTH) / (VIDEO_HEIGHT * 2); int sw, sh; if (w1 == 0 || h1 == 0) { @@ -750,11 +749,11 @@ void UI::wndScreen(bool *p_open) { if (texture) { ImGuiIO &io = ImGui::GetIO(); - auto sz = ImVec2((float)(VIDEO_WIDTH * e), (float)(VIDEO_HEIGHT * e)); + auto sz = ImVec2((float)(VIDEO_WIDTH * e), (float)(VIDEO_HEIGHT * 2 * e)); ImGui::InvisibleButton("##imgbtn", sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle); const ImVec2 p0 = ImGui::GetItemRectMin(); const ImVec2 p1 = ImGui::GetItemRectMax(); - auto pos = (io.MousePos - p0) / (p1 - p0) * ImVec2(VIDEO_WIDTH, VIDEO_HEIGHT) - ImVec2(16, 16); + auto pos = (io.MousePos - p0) / (p1 - p0) * ImVec2(VIDEO_WIDTH / 2, VIDEO_HEIGHT) - ImVec2(16, 16); int mx = std::max(0, std::min((int)pos.x, 319)); int my = std::max(0, std::min((int)pos.y, 199)); diff --git a/System/emulator/Video.cpp b/System/emulator/Video.cpp index 00d8e0c6..55f1cd72 100644 --- a/System/emulator/Video.cpp +++ b/System/emulator/Video.cpp @@ -1,18 +1,6 @@ #include "Video.h" #include "EmuState.h" -enum { - VCTRL_TEXT_ENABLE = (1 << 0), - VCTRL_MODE_OFF = (0 << 1), - VCTRL_MODE_TILEMAP = (1 << 1), - VCTRL_MODE_BITMAP = (2 << 1), - VCTRL_MODE_BITMAP_4BPP = (3 << 1), - VCTRL_MODE_MASK = (3 << 1), - VCTRL_SPRITES_ENABLE = (1 << 3), - VCTRL_TEXT_PRIORITY = (1 << 4), - VCTRL_REMAP_BORDER_CHAR = (1 << 5), -}; - Video::Video() { } @@ -21,32 +9,40 @@ void Video::drawLine() { if (line < 0 || line >= VIDEO_HEIGHT) return; + bool vActive = line >= 16 && line < 216; + // Render text - uint8_t lineText[512]; - unsigned idx = 512 - 16; + uint8_t lineText[1024]; + { + unsigned idx = 1024 - 32; + for (int i = 0; i < VIDEO_WIDTH; i++) { + // Draw text character + unsigned addr = 0; + int pixidx = i; + + if (vActive && idx < 640) { + int row = (line - 16) / 8; + if (emuState.videoCtrl & VCTRL_80_COLUMNS) { + int column = (i - 32) / 8; + addr = row * 80 + column; + } else { + int column = ((i / 2) - 16) / 8; + addr = row * 40 + column; + pixidx /= 2; + } - bool vActive = line >= 16 && line < 216; + } else if (emuState.videoCtrl & VCTRL_REMAP_BORDER_CHAR) { + addr = (emuState.videoCtrl & VCTRL_80_COLUMNS) ? 0x7FF : 0x3FF; + } - for (int i = 0; i < VIDEO_WIDTH; i++) { - // Draw text character - uint8_t ch; - uint8_t color; - if (vActive && idx < 320) { - int row = (line - 16) / 8; - int column = (i - 16) / 8; - ch = emuState.screenRam[row * 40 + column]; - color = emuState.colorRam[row * 40 + column]; - - } else { - unsigned borderAddr = (emuState.videoCtrl & VCTRL_REMAP_BORDER_CHAR) ? 0x3FF : 0; - - ch = emuState.screenRam[borderAddr]; - color = emuState.colorRam[borderAddr]; - } + uint8_t ch = emuState.screenRam[addr]; + uint8_t color = emuState.colorRam[addr]; + + uint8_t charBm = emuState.charRam[ch * 8 + (line & 7)]; - uint8_t charBm = emuState.charRam[ch * 8 + (line & 7)]; - lineText[idx] = (charBm & (1 << (7 - (i & 7)))) ? (color >> 4) : (color & 0xF); - idx = (idx + 1) & 511; + lineText[idx] = (charBm & (1 << (7 - (pixidx & 7)))) ? (color >> 4) : (color & 0xF); + idx = (idx + 1) & 1023; + } } // Render bitmap/tile layer @@ -83,7 +79,7 @@ void Video::drawLine() { case VCTRL_MODE_TILEMAP: { // Tile mode - idx = (-(emuState.videoScrX & 7)) & 511; + unsigned idx = (-(emuState.videoScrX & 7)) & 511; unsigned tileLine = (bmline + emuState.videoScrY) & 255; unsigned row = (tileLine >> 3) & 31; unsigned col = emuState.videoScrX >> 3; @@ -168,7 +164,7 @@ void Video::drawLine() { uint8_t palette = sprAttr & 0x30; bool priority = (sprAttr & (1 << 6)) != 0; - idx = sprX; + unsigned idx = sprX; if (vFlip) sprLine ^= (h16 ? 15 : 7); @@ -219,28 +215,33 @@ void Video::drawLine() { } // Compose layers - uint16_t *pd = &screen[line * VIDEO_WIDTH]; - idx = 512 - 16; - - for (int i = 0; i < VIDEO_WIDTH; i++) { - bool active = idx < 320 && vActive; - bool textPriority = (emuState.videoCtrl & VCTRL_TEXT_PRIORITY) != 0; - bool textEnable = (emuState.videoCtrl & VCTRL_TEXT_ENABLE) != 0; - - uint8_t colIdx = 0; - if (!active) { - if (textEnable) - colIdx = lineText[idx]; - } else { - if (textEnable && !textPriority) - colIdx = lineText[idx]; - if (!textEnable || textPriority || (lineGfx[idx] & 0xF) != 0) - colIdx = lineGfx[idx]; - if (textEnable && textPriority && (lineText[idx] & 0xF) != 0) - colIdx = lineText[idx]; - } + { + uint16_t *pd = &screen[line * VIDEO_WIDTH]; + unsigned idx = 1024 - 32; + + for (int i = 0; i < VIDEO_WIDTH; i++) { + bool active = idx < 640 && vActive; + bool textPriority = (emuState.videoCtrl & VCTRL_TEXT_PRIORITY) != 0; + bool textEnable = (emuState.videoCtrl & VCTRL_TEXT_ENABLE) != 0; + + uint8_t colIdx = 0; + if (!active) { + if (textEnable) + colIdx = lineText[idx]; + } else { + if (textEnable) + colIdx = lineText[idx]; + + if (textEnable && !textPriority) + colIdx = lineText[idx]; + if (!textEnable || textPriority || (lineGfx[idx / 2] & 0xF) != 0) + colIdx = lineGfx[idx / 2]; + if (textEnable && textPriority && (lineText[idx] & 0xF) != 0) + colIdx = lineText[idx]; + } - pd[i] = emuState.videoPalette[colIdx & 0x3F]; - idx = (idx + 1) & 511; + pd[i] = emuState.videoPalette[colIdx & 0x3F]; + idx = (idx + 1) & 1023; + } } } diff --git a/System/emulator/Video.h b/System/emulator/Video.h index 4f28faa1..7cf32c14 100644 --- a/System/emulator/Video.h +++ b/System/emulator/Video.h @@ -3,6 +3,20 @@ #include "Common.h" #include "SDL.h" +enum { + VCTRL_TEXT_ENABLE = (1 << 0), + VCTRL_MODE_OFF = (0 << 1), + VCTRL_MODE_TILEMAP = (1 << 1), + VCTRL_MODE_BITMAP = (2 << 1), + VCTRL_MODE_BITMAP_4BPP = (3 << 1), + VCTRL_MODE_MASK = (3 << 1), + VCTRL_SPRITES_ENABLE = (1 << 3), + VCTRL_TEXT_PRIORITY = (1 << 4), + VCTRL_REMAP_BORDER_CHAR = (1 << 5), + VCTRL_80_COLUMNS = (1 << 6), + VCTRL_TRAM_PAGE = (1 << 7), +}; + class Video { public: Video();