From 978531c95ef8c98ec86b9ba064872007b586b728 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Sun, 4 Jun 2017 22:28:52 -0500 Subject: [PATCH] Fixed keyboard handling, consolidated video handling. The keyboard handler now properly emulates the //e's 2-key rollover. However, we might have to add in a key repeat delay if it makes it so that you can't type on the console anymore. This had to be done because SDL will throw in key delay if you rely on it's SDL_KEYDOWN message and this caused many games with keyboard input to be unplayable. Also, we consolidated the video handling files, as there was very little in video.cpp and so we moved it into applevideo.cpp and renamed that file to video.cpp. Things are a lot cleaner as a result of this merge. :-) --- Makefile | 3 +- src/apple2.cpp | 460 +++++++++---------- src/applevideo.cpp | 923 -------------------------------------- src/applevideo.h | 26 -- src/dis65c02.cpp | 45 +- src/floppy.cpp | 3 +- src/gui/diskselector.cpp | 2 +- src/gui/gui.cpp | 1 - src/mmu.cpp | 6 +- src/video.cpp | 943 ++++++++++++++++++++++++++++++++++++++- src/video.h | 29 +- 11 files changed, 1211 insertions(+), 1230 deletions(-) delete mode 100644 src/applevideo.cpp delete mode 100644 src/applevideo.h diff --git a/Makefile b/Makefile index 27041df..9006897 100644 --- a/Makefile +++ b/Makefile @@ -105,9 +105,8 @@ OBJS = \ obj/font12pt.o \ obj/font14pt.o \ obj/gui.o \ - \ + \ obj/apple2-icon-64x64.o \ - obj/applevideo.o \ obj/ay8910.o \ obj/charset.o \ obj/dis65c02.o \ diff --git a/src/apple2.cpp b/src/apple2.cpp index 36bf0b0..b15113f 100644 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -2,7 +2,7 @@ // Apple 2 SDL Portable Apple Emulator // // by James Hammons -// © 2014 Underground Software +// © 2017 Underground Software // // Loosely based on AppleWin by Tom Charlesworth which was based on AppleWin by // Oliver Schmidt which was based on AppleWin by Michael O'Brien. :-) Parts are @@ -34,23 +34,21 @@ #include #include -#include #include #include #include #include +#include #include -#include "log.h" -#include "video.h" -#include "sound.h" -#include "settings.h" -#include "v65c02.h" -#include "applevideo.h" -#include "timing.h" -#include "floppy.h" #include "firmware.h" +#include "floppy.h" +#include "log.h" #include "mmu.h" - +#include "settings.h" +#include "sound.h" +#include "timing.h" +#include "v65c02.h" +#include "video.h" #include "gui/gui.h" // Debug and misc. defines @@ -95,6 +93,12 @@ static bool pauseMode = false; static bool fullscreenDebounce = false; static bool capsLock = false; static bool capsLockDebounce = false; +static bool resetKeyDown = false; + +// Vars to handle the //e's 2-key rollover +static SDL_Keycode keysHeld[2]; +static uint8_t keysHeldAppleCode[2]; +static uint8_t keyDownCount = 0; // Local functions @@ -110,18 +114,16 @@ static void BlinkTimer(void); #ifdef THREADED_65C02 // Test of threaded execution of 6502 static SDL_Thread * cpuThread = NULL; -//static SDL_mutex * cpuMutex = NULL; static SDL_cond * cpuCond = NULL; static SDL_sem * mainSem = NULL; static bool cpuFinished = false; -//static bool cpuSleep = false; // NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz // Let's try a thread... // // Here's how it works: Execute 1 frame's worth, then sleep. Other stuff wakes -// it up +// it up. // int CPUThreadFunc(void * data) { @@ -129,26 +131,20 @@ int CPUThreadFunc(void * data) // Also, must be created in the thread that uses it... SDL_mutex * cpuMutex = SDL_CreateMutex(); -// decrement mainSem... -//SDL_SemWait(mainSem); #ifdef CPU_THREAD_OVERFLOW_COMPENSATION float overflow = 0.0; #endif do { -// This is never set to true anywhere... -// if (cpuSleep) -// SDL_CondWait(cpuCond, cpuMutex); - // decrement mainSem... #ifdef THREAD_DEBUGGING WriteLog("CPU: SDL_SemWait(mainSem);\n"); #endif SDL_SemWait(mainSem); -// There are exactly 800 slices of 21.333 cycles per frame, so it works out -// evenly. + // There are exactly 800 slices of 21.333 cycles per frame, so it works + // out evenly. #ifdef THREAD_DEBUGGING WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n"); #endif @@ -163,6 +159,11 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n"); overflow -= 1.0; } + // If the CTRL+Reset key combo is being held, make sure the RESET + // line stays asserted: + if (resetKeyDown) + mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET; + Execute65C02(&mainCPU, cycles); WriteSampleToBuffer(); @@ -174,7 +175,7 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n"); WriteLog("CPU: SDL_mutexP(cpuMutex);\n"); #endif SDL_mutexP(cpuMutex); -// increment mainSem... + // increment mainSem... #ifdef THREAD_DEBUGGING WriteLog("CPU: SDL_SemPost(mainSem);\n"); #endif @@ -374,7 +375,7 @@ int main(int /*argc*/, char * /*argv*/[]) { InitLog("./apple2.log"); LoadSettings(); - srand(time(NULL)); // Initialize RNG + srand(time(NULL)); // Initialize RNG // Zero out memory memset(ram, 0, 0x10000); @@ -401,14 +402,14 @@ int main(int /*argc*/, char * /*argv*/[]) if (!InitVideo()) { - std::cout << "Aborting!" << std::endl; + printf("Could not init screen: aborting!\n"); return -1; } GUI::Init(sdlRenderer); + WriteLog("About to initialize audio...\n"); + SoundInit(); - // Have to do this *after* video init but *before* sound init...! -//Shouldn't be necessary since we're not doing emulation in the ISR... if (settings.autoStateSaving) { // Load last state from file... @@ -416,16 +417,16 @@ int main(int /*argc*/, char * /*argv*/[]) WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath); } - WriteLog("About to initialize audio...\n"); - SoundInit(); - SetupBlurTable(); // Set up the color TV emulation blur table - running = true; // Set running status... - InitializeEventList(); // Clear the event list before we use it... - SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval - SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals + running = true; + InitializeEventList(); + // Set frame to fire at 1/60 s interval + SetCallbackTime(FrameCallback, 16666.66666667); + // Set up blinking at 1/4 s intervals + SetCallbackTime(BlinkTimer, 250000); startTicks = SDL_GetTicks(); #ifdef THREADED_65C02 + // Kick off the CPU... cpuCond = SDL_CreateCond(); mainSem = SDL_CreateSemaphore(1); cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL); @@ -538,16 +539,69 @@ Z $DA $9A $DA $9A ESC $9B $9B $9B $9B No xlation */ +// +// Apple //e scancodes. Tables are normal (0), +CTRL (1), +SHIFT (2), +// +CTRL+SHIFT (3). Order of keys is: +// Delete, left, tab, down, up, return, right, escape +// Space, single quote, comma, minus, period, slash +// Numbers 0-9 +// Semicolon, equals, left bracket, backslash, right bracket, backquote +// Letters a-z (lowercase) +// +// N.B.: The Apple //e keyboard maps its shift characters like most modern US +// keyboards, so this table should suffice for the shifted keys just fine. +// +uint8_t apple2e_keycode[4][56] = { + { // Normal + 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B, + 0x20, 0x27, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3B, 0x3D, 0x5B, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A + }, + { // With CTRL held + 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B, + 0x20, 0x27, 0x2C, 0x1F, 0x2E, 0x2F, + 0x30, 0x31, 0x00, 0x33, 0x34, 0x35, 0x1E, 0x37, 0x38, 0x39, + 0x3B, 0x3D, 0x1B, 0x1C, 0x1D, 0x60, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A + }, + { // With Shift held + 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B, + 0x20, 0x22, 0x3C, 0x5F, 0x3E, 0x3F, + 0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, + 0x3A, 0x2B, 0x7B, 0x7C, 0x7D, 0x7E, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, + 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A + }, + { // With CTRL+Shift held + 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B, + 0x20, 0x22, 0x3C, 0x1F, 0x3E, 0x3F, + 0x29, 0x21, 0x00, 0x23, 0x24, 0x25, 0x1E, 0x26, 0x2A, 0x28, + 0x3A, 0x2B, 0x1B, 0x1C, 0x1D, 0x7E, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A + } +}; + static uint32_t frameCount = 0; static void FrameCallback(void) { SDL_Event event; + uint8_t keyIndex; while (SDL_PollEvent(&event)) { switch (event.type) { // Problem with using SDL_TEXTINPUT is that it causes key delay. :-/ +// We get key delay regardless... :-/ #if 0 case SDL_TEXTINPUT: //Need to do some key translation here, and screen out non-apple keys as well... @@ -568,6 +622,10 @@ static void FrameCallback(void) break; #endif case SDL_KEYDOWN: + // We do our own repeat handling thank you very much! :-) + if (event.key.repeat != 0) + break; + // Use CTRL+SHIFT+Q to exit, as well as the usual window decoration // method if ((event.key.keysym.mod & KMOD_CTRL) @@ -580,196 +638,109 @@ static void FrameCallback(void) return; } - // CTRL+RESET key emulation (mapped to CTRL+`) -// This doesn't work... -// if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL)) -// if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL)) + // CTRL+RESET key emulation (mapped to CTRL+HOME) if ((event.key.keysym.mod & KMOD_CTRL) - && (event.key.keysym.sym == SDLK_BACKQUOTE)) + && (event.key.keysym.sym == SDLK_HOME)) { -//NOTE that this shouldn't take place until the key is lifted... !!! FIX !!! -//ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card... - mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET; +//seems to leave the machine in an inconsistent state vis-a-vis the language card... [does it anymore?] + resetKeyDown = true; break; } - if (event.key.keysym.sym == SDLK_RIGHT) - lastKeyPressed = 0x15, keyDown = true; - else if (event.key.keysym.sym == SDLK_LEFT) - lastKeyPressed = 0x08, keyDown = true; - else if (event.key.keysym.sym == SDLK_UP) - lastKeyPressed = 0x0B, keyDown = true; - else if (event.key.keysym.sym == SDLK_DOWN) - lastKeyPressed = 0x0A, keyDown = true; - else if (event.key.keysym.sym == SDLK_RETURN) - lastKeyPressed = 0x0D, keyDown = true; - else if (event.key.keysym.sym == SDLK_ESCAPE) - lastKeyPressed = 0x1B, keyDown = true; - else if (event.key.keysym.sym == SDLK_BACKSPACE) - lastKeyPressed = 0x7F, keyDown = true; + // There has GOT to be a better way off mapping SDLKs to our + // keyindex. But for now, this should suffice. + keyIndex = 0xFF; - // Fix CTRL+key combo... - if (event.key.keysym.mod & KMOD_CTRL) + switch (event.key.keysym.sym) { - if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) - { - lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1; - keyDown = true; -//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40); - break; - } + case SDLK_BACKSPACE: keyIndex = 0; break; + case SDLK_LEFT: keyIndex = 1; break; + case SDLK_TAB: keyIndex = 2; break; + case SDLK_DOWN: keyIndex = 3; break; + case SDLK_UP: keyIndex = 4; break; + case SDLK_RETURN: keyIndex = 5; break; + case SDLK_RIGHT: keyIndex = 6; break; + case SDLK_ESCAPE: keyIndex = 7; break; + case SDLK_SPACE: keyIndex = 8; break; + case SDLK_QUOTE: keyIndex = 9; break; + case SDLK_COMMA: keyIndex = 10; break; + case SDLK_MINUS: keyIndex = 11; break; + case SDLK_PERIOD: keyIndex = 12; break; + case SDLK_SLASH: keyIndex = 13; break; + case SDLK_0: keyIndex = 14; break; + case SDLK_1: keyIndex = 15; break; + case SDLK_2: keyIndex = 16; break; + case SDLK_3: keyIndex = 17; break; + case SDLK_4: keyIndex = 18; break; + case SDLK_5: keyIndex = 19; break; + case SDLK_6: keyIndex = 20; break; + case SDLK_7: keyIndex = 21; break; + case SDLK_8: keyIndex = 22; break; + case SDLK_9: keyIndex = 23; break; + case SDLK_SEMICOLON: keyIndex = 24; break; + case SDLK_EQUALS: keyIndex = 25; break; + case SDLK_LEFTBRACKET: keyIndex = 26; break; + case SDLK_BACKSLASH: keyIndex = 27; break; + case SDLK_RIGHTBRACKET: keyIndex = 28; break; + case SDLK_BACKQUOTE: keyIndex = 29; break; + case SDLK_a: keyIndex = 30; break; + case SDLK_b: keyIndex = 31; break; + case SDLK_c: keyIndex = 32; break; + case SDLK_d: keyIndex = 33; break; + case SDLK_e: keyIndex = 34; break; + case SDLK_f: keyIndex = 35; break; + case SDLK_g: keyIndex = 36; break; + case SDLK_h: keyIndex = 37; break; + case SDLK_i: keyIndex = 38; break; + case SDLK_j: keyIndex = 39; break; + case SDLK_k: keyIndex = 40; break; + case SDLK_l: keyIndex = 41; break; + case SDLK_m: keyIndex = 42; break; + case SDLK_n: keyIndex = 43; break; + case SDLK_o: keyIndex = 44; break; + case SDLK_p: keyIndex = 45; break; + case SDLK_q: keyIndex = 46; break; + case SDLK_r: keyIndex = 47; break; + case SDLK_s: keyIndex = 48; break; + case SDLK_t: keyIndex = 49; break; + case SDLK_u: keyIndex = 50; break; + case SDLK_v: keyIndex = 51; break; + case SDLK_w: keyIndex = 52; break; + case SDLK_x: keyIndex = 53; break; + case SDLK_y: keyIndex = 54; break; + case SDLK_z: keyIndex = 55; break; } -#if 1 - // Fix SHIFT+key combo... - if (event.key.keysym.mod & KMOD_SHIFT) + // Stuff the key in if we have a valid one... + if (keyIndex != 0xFF) { - if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) - { - lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 0x41; - keyDown = true; -//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40); - break; - } - else if (event.key.keysym.sym == SDLK_1) - { - lastKeyPressed = '!'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_2) - { - lastKeyPressed = '@'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_3) - { - lastKeyPressed = '#'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_4) - { - lastKeyPressed = '$'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_5) - { - lastKeyPressed = '%'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_6) - { - lastKeyPressed = '^'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_7) - { - lastKeyPressed = '&'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_8) - { - lastKeyPressed = '*'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_9) - { - lastKeyPressed = '('; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_0) - { - lastKeyPressed = ')'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_MINUS) - { - lastKeyPressed = '_'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_EQUALS) - { - lastKeyPressed = '+'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_LEFTBRACKET) - { - lastKeyPressed = '{'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_RIGHTBRACKET) - { - lastKeyPressed = '}'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_BACKSLASH) - { - lastKeyPressed = '|'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_SEMICOLON) - { - lastKeyPressed = ':'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_QUOTE) - { - lastKeyPressed = '"'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_COMMA) - { - lastKeyPressed = '<'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_PERIOD) - { - lastKeyPressed = '>'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_SLASH) - { - lastKeyPressed = '?'; - keyDown = true; - break; - } - else if (event.key.keysym.sym == SDLK_BACKQUOTE) - { - lastKeyPressed = '~'; - keyDown = true; - break; - } - } -#endif + // Handle Shift, CTRL, & Shift+CTRL combos + uint8_t table = 0; - // General keys... - if (event.key.keysym.sym >= SDLK_SPACE && event.key.keysym.sym <= SDLK_z) - { - lastKeyPressed = event.key.keysym.sym; + if (event.key.keysym.mod & KMOD_CTRL) + table |= 1; + + if (event.key.keysym.mod & KMOD_SHIFT) + table |= 2; + + lastKeyPressed = apple2e_keycode[table][keyIndex]; keyDown = true; - // Check for Caps Lock key... - if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z && capsLock) - lastKeyPressed -= 0x20; + keyDownCount++; + // Buffer the key held. Note that the last key is always + // stuffed into keysHeld[0]. + if (keyDownCount >= 2) + { + keysHeld[1] = keysHeld[0]; + keysHeldAppleCode[1] = keysHeldAppleCode[0]; + + if (keyDownCount > 2) + keyDownCount = 2; + } + + keysHeld[0] = event.key.keysym.sym; + keysHeldAppleCode[0] = lastKeyPressed; break; } @@ -788,15 +759,14 @@ static void FrameCallback(void) SpawnMessage("*** RESUME ***"); } } - - // Paddle buttons 0 & 1 - if (event.key.keysym.sym == SDLK_LALT) + // Buttons 0 & 1 + else if (event.key.keysym.sym == SDLK_LALT) openAppleDown = true; - if (event.key.keysym.sym == SDLK_RALT) + else if (event.key.keysym.sym == SDLK_RALT) closedAppleDown = true; - - if (event.key.keysym.sym == SDLK_F11) - dumpDis = !dumpDis; // Toggle the disassembly process + // Toggle the disassembly process + else if (event.key.keysym.sym == SDLK_F11) + dumpDis = !dumpDis; /*else if (event.key.keysym.sym == SDLK_F9) { @@ -809,7 +779,7 @@ static void FrameCallback(void) // SpawnMessage("Image swapped..."); }//*/ - if (event.key.keysym.sym == SDLK_F2) + else if (event.key.keysym.sym == SDLK_F2) TogglePalette(); else if (event.key.keysym.sym == SDLK_F3) CycleScreenTypes(); @@ -841,8 +811,7 @@ static void FrameCallback(void) fullscreenDebounce = true; } } - - if (event.key.keysym.sym == SDLK_CAPSLOCK) + else if (event.key.keysym.sym == SDLK_CAPSLOCK) { if (!capsLockDebounce) { @@ -852,38 +821,75 @@ static void FrameCallback(void) } break; + case SDL_KEYUP: if (event.key.keysym.sym == SDLK_F12) fullscreenDebounce = false; - if (event.key.keysym.sym == SDLK_CAPSLOCK) + else if (event.key.keysym.sym == SDLK_CAPSLOCK) capsLockDebounce = false; - // Paddle buttons 0 & 1 - if (event.key.keysym.sym == SDLK_LALT) + else if (event.key.keysym.sym == SDLK_LALT) openAppleDown = false; - if (event.key.keysym.sym == SDLK_RALT) + else if (event.key.keysym.sym == SDLK_RALT) closedAppleDown = false; + else if ((event.key.keysym.mod & KMOD_CTRL) + && (event.key.keysym.sym == SDLK_HOME)) + resetKeyDown = false; + else + { + // Handle key buffering 'key up' event (2 key rollover) + if ((keyDownCount == 1) && (event.key.keysym.sym == keysHeld[0])) + { + keyDownCount--; + } + else if (keyDownCount == 2) + { + if (event.key.keysym.sym == keysHeld[0]) + { + keyDownCount--; + keysHeld[0] = keysHeld[1]; + keysHeldAppleCode[0] = keysHeldAppleCode[1]; + } + else if (event.key.keysym.sym == keysHeld[1]) + { + keyDownCount--; + } + } + } break; + case SDL_MOUSEBUTTONDOWN: GUI::MouseDown(event.motion.x, event.motion.y, event.motion.state); break; + case SDL_MOUSEBUTTONUP: GUI::MouseUp(event.motion.x, event.motion.y, event.motion.state); break; + case SDL_MOUSEMOTION: GUI::MouseMove(event.motion.x, event.motion.y, event.motion.state); break; + case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_LEAVE) GUI::MouseMove(0, 0, 0); break; + case SDL_QUIT: running = false; } } + // Stuff the Apple keyboard buffer, if any keys are pending + // N.B.: May have to simulate the key repeat delay too + if (keyDownCount > 0) + { + lastKeyPressed = keysHeldAppleCode[0]; + keyDown = true; + } + // Handle power request from the GUI if (powerStateChangeRequested) { @@ -904,8 +910,8 @@ static void FrameCallback(void) powerStateChangeRequested = false; } - RenderVideoFrame(); // Render Apple screen to buffer - RenderScreenBuffer(); // Render buffer to host screen + // Render the Apple screen + GUI overlay + RenderAppleScreen(sdlRenderer); GUI::Render(sdlRenderer); SDL_RenderPresent(sdlRenderer); SetCallbackTime(FrameCallback, 16666.66666667); @@ -928,7 +934,7 @@ if (counter == 60) // This is the problem: If you set the interval to 16, it runs faster than // 1/60s per frame. If you set it to 17, it runs slower. What we need is to // have it do 16 for one frame, then 17 for two others. Then it should average -// out to 1/60s per frame every 3 frames. +// out to 1/60s per frame every 3 frames. [And now it does!] frameCount = (frameCount + 1) % 3; uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0); diff --git a/src/applevideo.cpp b/src/applevideo.cpp deleted file mode 100644 index 54cbdb1..0000000 --- a/src/applevideo.cpp +++ /dev/null @@ -1,923 +0,0 @@ -// -// Apple 2 video support -// -// All the video modes that a real Apple 2 supports are handled here -// -// by James Hammons -// (c) 2005-2017 Underground Software -// -// JLH = James Hammons -// -// WHO WHEN WHAT -// --- ---------- ----------------------------------------------------------- -// JLH 12/01/2005 Added color TV/monochrome emulation to hi-res code -// JLH 12/09/2005 Cleaned up color TV emulation code -// JLH 12/09/2005 Fixed lo-res color TV/mono emulation modes -// -// STILL TO DO: -// -// - Fix LoRes mode green mono to skip every other scanline instead of fill -// like white mono does [DONE] -// - Double HiRes [DONE] -// - 80 column text [DONE] -// - Fix OSD text display so that it's visible no matter what background is there [DONE] -// - -// Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore] - -#include "applevideo.h" - -#include // for memset() -#include -#include // for va_* stuff -#include "apple2.h" -#include "charset.h" -#include "video.h" -#include "gui/font14pt.h" -#include "gui/gui.h" - -/* Reference: Technote tn-iigs-063 "Master Color Values" - - Color Color Register LR HR DHR Master Color R,G,B - Name Value # # # Value - ---------------------------------------------------- - Black 0 0 0,4 0 $0000 (0,0,0) -(Magenta) Deep Red 1 1 1 $0D03 (D,0,3) - Dark Blue 2 2 8 $0009 (0,0,9) - (Violet) Purple 3 3 2 9 $0D2D (D,2,D) - Dark Green 4 4 4 $0072 (0,7,2) - (Gray 1) Dark Gray 5 5 5 $0555 (5,5,5) - (Blue) Medium Blue 6 6 6 C $022F (2,2,F) - (Cyan) Light Blue 7 7 D $06AF (6,A,F) - Brown 8 8 2 $0850 (8,5,0) - Orange 9 9 5 3 $0F60 (F,6,0) - (Gray 2) Light Gray A A A $0AAA (A,A,A) - Pink B B B $0F98 (F,9,8) - (Green) Light Green C C 1 6 $01D0 (1,D,0) - Yellow D D 7 $0FF0 (F,F,0) - (Aqua) Aquamarine E E E $04F9 (4,F,9) - White F F 3,7 F $0FFF (F,F,F) - - LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res */ - -// Global variables - -bool flash = false; -bool textMode = true; -bool mixedMode = false; -bool displayPage2 = false; -bool hiRes = false; -bool alternateCharset = false; -bool col80Mode = false; - -// Local variables - -// We set up the colors this way so that they'll be endian safe -// when we cast them to a uint32_t. Note that the format is RGBA. - -// "Master Color Values" palette - -static uint8_t colors[16 * 4] = { - 0x00, 0x00, 0x00, 0xFF, // Black - 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta) - 0x00, 0x00, 0x99, 0xFF, // Dark Blue - 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet) - 0x00, 0x77, 0x22, 0xFF, // Dark Green - 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1) - 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue) - 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan) - 0x88, 0x55, 0x00, 0xFF, // Brown - 0xFF, 0x66, 0x00, 0xFF, // Orange - 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2) - 0xFF, 0x99, 0x88, 0xFF, // Pink - 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green) - 0xFF, 0xFF, 0x00, 0xFF, // Yellow - 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua) - 0xFF, 0xFF, 0xFF, 0xFF // White -}; - -// This palette comes from ApplePC's colors (more realistic to my eye ;-) - -static uint8_t altColors[16 * 4] = { - 0x00, 0x00, 0x00, 0xFF, - 0x7D, 0x20, 0x41, 0xFF, - 0x41, 0x30, 0x7D, 0xFF, - 0xBE, 0x51, 0xBE, 0xFF, - 0x00, 0x5D, 0x3C, 0xFF, - 0x7D, 0x7D, 0x7D, 0xFF, - 0x41, 0x8E, 0xBA, 0xFF, - 0xBE, 0xAE, 0xFB, 0xFF, - 0x3C, 0x4D, 0x00, 0xFF, - 0xBA, 0x6D, 0x41, 0xFF, - 0x7D, 0x7D, 0x7D, 0xFF, - 0xFB, 0x9E, 0xBE, 0xFF, - 0x3C, 0xAA, 0x3C, 0xFF, - 0xBA, 0xCB, 0x7D, 0xFF, - 0x7D, 0xDB, 0xBA, 0xFF, - 0xFB, 0xFB, 0xFB, 0xFF }; - -// Lo-res starting line addresses - -static uint16_t lineAddrLoRes[24] = { - 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780, - 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8, - 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 }; - -// Hi-res starting line addresses - -static uint16_t lineAddrHiRes[192] = { - 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, - 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80, - 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00, - 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80, - - 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00, - 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80, - 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00, - 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80, - - 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28, - 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8, - 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28, - 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8, - - 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28, - 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8, - 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28, - 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8, - - 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50, - 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0, - 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50, - 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0, - - 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50, - 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0, - 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50, - 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 }; - -uint16_t appleHiresToMono[0x200] = { - 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00, - 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x - 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30, - 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x - 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C, - 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x - 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C, - 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x - 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03, - 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x - 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33, - 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x - 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F, - 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x - 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F, - 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x - 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80, - 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x - 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98, - 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x - 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86, - 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax - 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E, - 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx - 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81, - 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx - 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99, - 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx - 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87, - 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex - 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F, - 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx - - // Second half adds in the previous byte's lo pixel - - 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00, - 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x - 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30, - 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x - 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C, - 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x - 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C, - 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x - 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03, - 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x - 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33, - 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x - 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F, - 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x - 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F, - 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x - 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80, - 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x - 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98, - 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x - 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86, - 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax - 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E, - 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx - 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81, - 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx - 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99, - 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx - 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87, - 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex - 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F, - 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx -}; - -//static uint8_t blurTable[0x800][8]; // Color TV blur table -static uint8_t blurTable[0x80][8]; // Color TV blur table -static uint8_t mirrorTable[0x100]; -static uint32_t * palette = (uint32_t *)altColors; -enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY }; -static uint8_t screenType = ST_COLOR_TV; - -// Local functions - -static void Render40ColumnTextLine(uint8_t line); -static void Render80ColumnTextLine(uint8_t line); -static void Render40ColumnText(void); -static void Render80ColumnText(void); -static void RenderLoRes(uint16_t toLine = 24); -static void RenderHiRes(uint16_t toLine = 192); -static void RenderDHiRes(uint16_t toLine = 192); - - -void SetupBlurTable(void) -{ - // NOTE: This table only needs to be 7 bits wide instead of 11, since the - // last four bits are copies of the previous four... - // Odd. Doing the bit patterns from 0-$7F doesn't work, but going - // from 0-$7FF stepping by 16 does. Hm. - // Well, it seems that going from 0-$7F doesn't have enough precision to do the job. -#if 0 -// for(uint16_t bitPat=0; bitPat<0x800; bitPat++) - for(uint16_t bitPat=0; bitPat<0x80; bitPat++) - { -/* uint16_t w3 = bitPat & 0x888; - uint16_t w2 = bitPat & 0x444; - uint16_t w1 = bitPat & 0x222; - uint16_t w0 = bitPat & 0x111;*/ - uint16_t w3 = bitPat & 0x88; - uint16_t w2 = bitPat & 0x44; - uint16_t w1 = bitPat & 0x22; - uint16_t w0 = bitPat & 0x11; - - uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF; - uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF; - uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF; - uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF; - - for(int8_t i=7; i>=0; i--) - { - uint8_t color = (((blurred0 >> i) & 0x01) << 3) - | (((blurred1 >> i) & 0x01) << 2) - | (((blurred2 >> i) & 0x01) << 1) - | ((blurred3 >> i) & 0x01); - blurTable[bitPat][7 - i] = color; - } - } -#else - for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10) - { - uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888; - - uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF; - uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF; - uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF; - uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF; - - for(int8_t i=7; i>=0; i--) - { - uint8_t color = (((blurred0 >> i) & 0x01) << 3) - | (((blurred1 >> i) & 0x01) << 2) - | (((blurred2 >> i) & 0x01) << 1) - | ((blurred3 >> i) & 0x01); - blurTable[bitPat >> 4][7 - i] = color; - } - } -#endif - - for(int i=0; i<256; i++) - { - mirrorTable[i] = ((i & 0x01) << 7) - | ((i & 0x02) << 5) - | ((i & 0x04) << 3) - | ((i & 0x08) << 1) - | ((i & 0x10) >> 1) - | ((i & 0x20) >> 3) - | ((i & 0x40) >> 5) - | ((i & 0x80) >> 7); - } -} - - -void TogglePalette(void) -{ - if (palette == (uint32_t *)colors) - { - palette = (uint32_t *)altColors; - SpawnMessage("Color TV palette"); - } - else - { - palette = (uint32_t *)colors; - SpawnMessage("\"Master Color Values\" palette"); - } -} - - -void CycleScreenTypes(void) -{ - char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" }; - - screenType++; - - if (screenType == ST_LAST_ENTRY) - screenType = ST_FIRST_ENTRY; - - SpawnMessage("%s", scrTypeStr[screenType]); -} - - -static uint32_t msgTicks = 0; -static char message[4096]; - -void SpawnMessage(const char * text, ...) -{ - va_list arg; - - va_start(arg, text); - vsprintf(message, text, arg); - va_end(arg); - - msgTicks = 120; -} - - -static void DrawString2(uint32_t x, uint32_t y, uint32_t color); -static void DrawString(void) -{ -//This approach works, and seems to be fast enough... Though it probably would -//be better to make the oversized font to match this one... - for(uint32_t x=7; x<=9; x++) - for(uint32_t y=7; y<=9; y++) - DrawString2(x, y, 0x00000000); - - DrawString2(8, 8, 0x0020FF20); -} - - -static void DrawString2(uint32_t x, uint32_t y, uint32_t color) -{ -//uint32_t x = 8, y = 8; - uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH); -// uint32_t color = 0x0020FF20; -//This could be done ahead of time, instead of on each pixel... -//(Now it is!) - uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF; - - for(uint32_t i=0; i> 16) & 0xFF, - eGreen = (existingColor >> 8) & 0xFF, - eRed = existingColor & 0xFF; - -//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries) -//Here we've modified it to have 33 levels of transparency (could have any # we want!) -//because dividing by 32 is faster than dividing by 31...! - uint8_t invTrans = 255 - trans; - - uint32_t bRed = (eRed * invTrans + nRed * trans) / 255; - uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255; - uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255; - -//THIS IS NOT ENDIAN SAFE -//NB: Setting the alpha channel here does nothing. - *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed; - } - } - } - - address += FONT_WIDTH; - } -} - - -static void Render40ColumnTextLine(uint8_t line) -{ - uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF); - - for(int x=0; x<40; x++) - { - uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x]; - - // Render character at (x, y) - - for(int cy=0; cy<8; cy++) - { - for(int cx=0; cx<7; cx++) - { - uint32_t pixel = 0xFF000000; - - if (alternateCharset) - { - if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)]) - pixel = pixelOn; - - if (chr < 0x80) - pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF); - - if ((chr & 0xC0) == 0x40 && flash) - pixel = 0xFF000000; - } - else - { - if (textChar2e[(chr * 56) + cx + (cy * 7)]) - pixel = pixelOn; - } - - scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel; - scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel; - - // QnD method to get blank alternate lines in text mode - if (screenType == ST_GREEN_MONO) - pixel = 0xFF000000; - - { - scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel; - scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel; - } - } - } - } -} - - -static void Render80ColumnTextLine(uint8_t line) -{ - uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF); - - for(int x=0; x<80; x++) - { -#if 0 -// This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0 - uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x]; - - if (x > 39) - chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40]; -#else - uint8_t chr; - - if (x & 0x01) - chr = ram[lineAddrLoRes[line] + (x >> 1)]; - else - chr = ram2[lineAddrLoRes[line] + (x >> 1)]; -#endif - - // Render character at (x, y) - - for(int cy=0; cy<8; cy++) - { - for(int cx=0; cx<7; cx++) - { - uint32_t pixel = 0xFF000000; - - if (alternateCharset) - { - if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)]) - pixel = pixelOn; - - if (chr < 0x80) - pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF); - - if ((chr & 0xC0) == 0x40 && flash) - pixel = 0xFF000000; - } - else - { - if (textChar2e[(chr * 56) + cx + (cy * 7)]) - pixel = pixelOn; - } - - scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel; - - // QnD method to get blank alternate lines in text mode - if (screenType == ST_GREEN_MONO) - pixel = 0xFF000000; - - scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel; - } - } - } -} - - -static void Render40ColumnText(void) -{ - for(uint8_t line=0; line<24; line++) - Render40ColumnTextLine(line); -} - - -static void Render80ColumnText(void) -{ - for(uint8_t line=0; line<24; line++) - Render80ColumnTextLine(line); -} - - -static void RenderLoRes(uint16_t toLine/*= 24*/) -{ -// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!! -// Also, we could set up three different Render functions depending on which -// render type was set and call it with a function pointer. Would be faster -// then the nested ifs we have now. -/* -Note that these colors correspond to the bit patterns generated by the numbers 0-F in order: -Color #s correspond to the bit patterns in reverse... Interesting! - -00 00 00 -> 0 [0000] -> 0 (lores color #) -3c 4d 00 -> 8 [0001] -> 8? BROWN -00 5d 3c -> 4 [0010] -> 4? DARK GREEN -3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN) -41 30 7d -> 2 [0100] -> 2? DARK BLUE -7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY -41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE) -7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA) -7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA) -ba 6d 41 -> 9 [1001] -> 9? ORANGE -7d 7d 7d -> 5 [1010] -> 5? DARK GRAY -ba cb 7d -> 13 [1011] -> 13? YELLOW -be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET) -fb 9e be -> 11 [1101] -> 11? PINK -be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN) -fb fb fb -> 15 [1111] -> 15 WHITE -*/ - uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; - -//This is the old "perfect monitor" rendering code... -/* if (screenType != ST_COLOR_TV) // Not correct, but for now... -//if (1) - { - for(uint16_t y=0; y> 4]; - - for(int cy=4; cy<8; cy++) - for(int cx=0; cx<14; cx++) - scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel; - } - } - } - else//*/ - - uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61); - - for(uint16_t y=0; y> 24; - pixels <<= 4; - - for(uint8_t j=0; j<4; j++) - { - uint8_t color = blurTable[bitPat][j]; - - for(uint32_t cy=0; cy<8; cy++) - { - scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; -// scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; - } - } - } - - previous3Bits = pixels & 0x70000000; - } - else - { - for(int j=0; j<28; j++) - { - for(uint32_t cy=0; cy<8; cy++) - { - scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); -// scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); - } - - pixels <<= 1; - } - } - } - - // Now do bottom half... - - previous3Bits = 0; - - for(uint16_t x=0; x<40; x+=2) - { - uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4; - uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4; - scrByte1 = mirrorNybble[scrByte1]; - scrByte2 = mirrorNybble[scrByte2]; - // This is just a guess, but it'll have to do for now... - uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16) - | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12) - | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2; - - // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF - // 0ppp 1111 1111 1111 11|11 1111 1111 1111 - // 31 27 23 19 15 11 7 3 0 - - if (screenType == ST_COLOR_TV) - { - for(uint8_t i=0; i<7; i++) - { - uint8_t bitPat = (pixels & 0x7F000000) >> 24; - pixels <<= 4; - - for(uint8_t j=0; j<4; j++) - { - uint8_t color = blurTable[bitPat][j]; - - for(uint32_t cy=8; cy<16; cy++) - { - scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; -// scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; - } - } - } - - previous3Bits = pixels & 0x70000000; - } - else - { - for(int j=0; j<28; j++) - { - for(uint32_t cy=8; cy<16; cy++) - { - scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); -// scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); - } - - pixels <<= 1; - } - } - } - } -} - - -static void RenderHiRes(uint16_t toLine/*= 192*/) -{ -//printf("RenderHiRes to line %u\n", toLine); -// NOTE: Not endian safe. !!! FIX !!! [DONE] -#if 0 - uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61); -#else -// Now it is. Now roll this fix into all the other places... !!! FIX !!! -// The colors are set in the 8-bit array as R G B A - uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF }; - uint32_t * colorPtr = (uint32_t *)monoColors; - uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]); -#endif - - for(uint16_t y=0; y> 24; - pixels <<= 4; - - for(uint8_t j=0; j<4; j++) - { - uint8_t color = blurTable[bitPat][j]; -#if 0 -//This doesn't seem to make things go any faster... -//It's the OpenGL render that's faster... Hmm... - scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color]; -#else - scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; - scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; -#endif - } - } - - previous3bits = pixels & 0x70000000; - } - else - { - for(int j=0; j<28; j++) - { - scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); - - if (screenType == ST_GREEN_MONO) - pixels &= 0x07FFFFFF; - - scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); - pixels <<= 1; - } - } - } - } -} - - -static void RenderDHiRes(uint16_t toLine/*= 192*/) -{ -// Now it is. Now roll this fix into all the other places... !!! FIX !!! -// The colors are set in the 8-bit array as R G B A - uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF }; - uint32_t * colorPtr = (uint32_t *)monoColors; - uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]); - - for(uint16_t y=0; y> 1); - - // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF - // 0ppp 1111 1111 1111 1111 1111 1111 1111 - // 31 27 23 19 15 11 7 3 0 - - if (screenType == ST_COLOR_TV) - { - for(uint8_t i=0; i<7; i++) - { - uint8_t bitPat = (pixels & 0xFE000000) >> 25; - pixels <<= 4; - - for(uint8_t j=0; j<4; j++) - { - uint32_t color = palette[blurTable[bitPat][j]]; - scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color; - scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color; - } - } - - previous4bits = pixels & 0xF0000000; - } - else - { - for(int j=0; j<28; j++) - { - scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); - - if (screenType == ST_GREEN_MONO) - pixels &= 0x07FFFFFF; - - scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); - pixels <<= 1; - } - } - } - } -} - - -void RenderVideoFrame(void) -{ - if (GUI::powerOnState == true) - { - if (textMode) - { - if (!col80Mode) - Render40ColumnText(); - else - Render80ColumnText(); - } - else - { - if (mixedMode) - { - if (hiRes) - { - RenderHiRes(160); - Render40ColumnTextLine(20); - Render40ColumnTextLine(21); - Render40ColumnTextLine(22); - Render40ColumnTextLine(23); - } - else - { - RenderLoRes(20); - Render40ColumnTextLine(20); - Render40ColumnTextLine(21); - Render40ColumnTextLine(22); - Render40ColumnTextLine(23); - } - } - else - { - if (dhires) - RenderDHiRes(); - else if (hiRes) - RenderHiRes(); - else - RenderLoRes(); - } - } - } - else - { - memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t)); - } - - if (msgTicks) - { - DrawString(); - msgTicks--; - } -} - diff --git a/src/applevideo.h b/src/applevideo.h deleted file mode 100644 index e0707ac..0000000 --- a/src/applevideo.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Apple 2 video support -// - -#ifndef __APPLEVIDEO_H__ -#define __APPLEVIDEO_H__ - -// Global variables (exported) - -extern bool flash; -extern bool textMode; -extern bool mixedMode; -extern bool displayPage2; -extern bool hiRes; -extern bool alternateCharset; -extern bool col80Mode; - -// Functions (exported) - -void SetupBlurTable(void); -void TogglePalette(void); -void CycleScreenTypes(void); -void SpawnMessage(const char * text, ...); -void RenderVideoFrame(void); - -#endif // __APPLEVIDEO_H__ diff --git a/src/dis65c02.cpp b/src/dis65c02.cpp index 4841630..5869781 100644 --- a/src/dis65c02.cpp +++ b/src/dis65c02.cpp @@ -6,10 +6,11 @@ // #include "dis65c02.h" + #include #include -#include "v65c02.h" #include "log.h" +#include "v65c02.h" // External shit @@ -79,7 +80,6 @@ static uint8_t mnemonics[256][5] = { static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst) { char buf[32]; -// WriteLog("%04X: ", src); sprintf(outbuf, "%04X: ", src); uint8_t cnt = 0; @@ -89,7 +89,6 @@ static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst) for(uint32_t i=src; i #include "apple2.h" #include "log.h" -#include "applevideo.h" // For message spawning... Though there's probably a better approach than this! +#include "video.h" // For message spawning... Though there's probably a + // better approach than this! // Useful enums diff --git a/src/gui/diskselector.cpp b/src/gui/diskselector.cpp index c605b13..dfdbd57 100644 --- a/src/gui/diskselector.cpp +++ b/src/gui/diskselector.cpp @@ -101,7 +101,7 @@ void DiskSelector::Init(SDL_Renderer * renderer) WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n"); for(uint32_t i=0; i // // WHO WHEN WHAT // --- ---------- ----------------------------------------------------------- -// JLH 01/04/2006 Added changelog ;-) -// JLH 01/20/2006 Cut out unnecessary buffering +// JLH 12/01/2005 Added color TV/monochrome emulation to hi-res code +// JLH 12/09/2005 Cleaned up color TV emulation code +// JLH 12/09/2005 Fixed lo-res color TV/mono emulation modes +// +// STILL TO DO: +// +// - Fix LoRes mode green mono to skip every other scanline instead of fill +// like white mono does [DONE] +// - Double HiRes [DONE] +// - 80 column text [DONE] +// - Fix OSD text display so that it's visible no matter what background is there [DONE] // +// Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore] + #include "video.h" -#include // (for memset, etc... Lazy!) -#include + +#include // for memset() +#include +#include // for va_* stuff +#include "apple2.h" #include "apple2-icon-64x64.h" +#include "charset.h" #include "log.h" #include "settings.h" +#include "gui/font14pt.h" +#include "gui/gui.h" +/* Reference: Technote tn-iigs-063 "Master Color Values" + + Color Color Register LR HR DHR Master Color R,G,B + Name Value # # # Value + ---------------------------------------------------- + Black 0 0 0,4 0 $0000 (0,0,0) +(Magenta) Deep Red 1 1 1 $0D03 (D,0,3) + Dark Blue 2 2 8 $0009 (0,0,9) + (Violet) Purple 3 3 2 9 $0D2D (D,2,D) + Dark Green 4 4 4 $0072 (0,7,2) + (Gray 1) Dark Gray 5 5 5 $0555 (5,5,5) + (Blue) Medium Blue 6 6 6 C $022F (2,2,F) + (Cyan) Light Blue 7 7 D $06AF (6,A,F) + Brown 8 8 2 $0850 (8,5,0) + Orange 9 9 5 3 $0F60 (F,6,0) + (Gray 2) Light Gray A A A $0AAA (A,A,A) + Pink B B B $0F98 (F,9,8) + (Green) Light Green C C 1 6 $01D0 (1,D,0) + Yellow D D 7 $0FF0 (F,F,0) + (Aqua) Aquamarine E E E $04F9 (4,F,9) + White F F 3,7 F $0FFF (F,F,F) + + LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res + + N.B.: These colors look like shit */ + +// Global variables + +bool flash = false; +bool textMode = true; +bool mixedMode = false; +bool displayPage2 = false; +bool hiRes = false; +bool alternateCharset = false; +bool col80Mode = false; +SDL_Renderer * sdlRenderer = NULL; + +// Local variables -// Local vars static SDL_Window * sdlWindow = NULL; static SDL_Texture * sdlTexture = NULL; +static uint32_t * scrBuffer; +static int scrPitch; -// Exported vars -SDL_Renderer * sdlRenderer = NULL; -uint32_t scrBuffer[VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t)]; +// We set up the colors this way so that they'll be endian safe +// when we cast them to a uint32_t. Note that the format is RGBA. + +// "Master Color Values" palette + +static uint8_t colors[16 * 4] = { + 0x00, 0x00, 0x00, 0xFF, // Black + 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta) + 0x00, 0x00, 0x99, 0xFF, // Dark Blue + 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet) + 0x00, 0x77, 0x22, 0xFF, // Dark Green + 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1) + 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue) + 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan) + 0x88, 0x55, 0x00, 0xFF, // Brown + 0xFF, 0x66, 0x00, 0xFF, // Orange + 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2) + 0xFF, 0x99, 0x88, 0xFF, // Pink + 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green) + 0xFF, 0xFF, 0x00, 0xFF, // Yellow + 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua) + 0xFF, 0xFF, 0xFF, 0xFF // White +}; + +// This palette comes from ApplePC's colors (more realistic to my eye ;-) + +static uint8_t altColors[16 * 4] = { + 0x00, 0x00, 0x00, 0xFF, + 0x7D, 0x20, 0x41, 0xFF, + 0x41, 0x30, 0x7D, 0xFF, + 0xBE, 0x51, 0xBE, 0xFF, + 0x00, 0x5D, 0x3C, 0xFF, + 0x7D, 0x7D, 0x7D, 0xFF, + 0x41, 0x8E, 0xBA, 0xFF, + 0xBE, 0xAE, 0xFB, 0xFF, + 0x3C, 0x4D, 0x00, 0xFF, + 0xBA, 0x6D, 0x41, 0xFF, + 0x7D, 0x7D, 0x7D, 0xFF, + 0xFB, 0x9E, 0xBE, 0xFF, + 0x3C, 0xAA, 0x3C, 0xFF, + 0xBA, 0xCB, 0x7D, 0xFF, + 0x7D, 0xDB, 0xBA, 0xFF, + 0xFB, 0xFB, 0xFB, 0xFF }; + +// Lo-res starting line addresses + +static uint16_t lineAddrLoRes[24] = { + 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780, + 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8, + 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 }; + +// Hi-res starting line addresses + +static uint16_t lineAddrHiRes[192] = { + 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, + 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80, + 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00, + 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80, + + 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00, + 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80, + 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00, + 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80, + + 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28, + 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8, + 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28, + 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8, + + 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28, + 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8, + 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28, + 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8, + + 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50, + 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0, + 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50, + 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0, + + 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50, + 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0, + 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50, + 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 }; + +uint16_t appleHiresToMono[0x200] = { + 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00, + 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x + 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30, + 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x + 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C, + 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x + 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C, + 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x + 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03, + 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x + 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33, + 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x + 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F, + 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x + 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F, + 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x + 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80, + 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x + 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98, + 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x + 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86, + 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax + 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E, + 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx + 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81, + 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx + 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99, + 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx + 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87, + 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex + 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F, + 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx + + // Second half adds in the previous byte's lo pixel + + 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00, + 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x + 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30, + 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x + 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C, + 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x + 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C, + 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x + 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03, + 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x + 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33, + 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x + 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F, + 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x + 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F, + 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x + 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80, + 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x + 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98, + 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x + 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86, + 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax + 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E, + 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx + 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81, + 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx + 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99, + 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx + 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87, + 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex + 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F, + 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx +}; + +//static uint8_t blurTable[0x800][8]; // Color TV blur table +static uint8_t blurTable[0x80][8]; // Color TV blur table +static uint8_t mirrorTable[0x100]; +static uint32_t * palette = (uint32_t *)altColors; +enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY }; +static uint8_t screenType = ST_COLOR_TV; + +// Local functions + +static void Render40ColumnTextLine(uint8_t line); +static void Render80ColumnTextLine(uint8_t line); +static void Render40ColumnText(void); +static void Render80ColumnText(void); +static void RenderLoRes(uint16_t toLine = 24); +static void RenderHiRes(uint16_t toLine = 192); +static void RenderDHiRes(uint16_t toLine = 192); +static void RenderVideoFrame(/*uint32_t *, int*/); + + +void SetupBlurTable(void) +{ + // NOTE: This table only needs to be 7 bits wide instead of 11, since the + // last four bits are copies of the previous four... + // Odd. Doing the bit patterns from 0-$7F doesn't work, but going + // from 0-$7FF stepping by 16 does. Hm. + // Well, it seems that going from 0-$7F doesn't have enough precision to do the job. +#if 0 +// for(uint16_t bitPat=0; bitPat<0x800; bitPat++) + for(uint16_t bitPat=0; bitPat<0x80; bitPat++) + { +/* uint16_t w3 = bitPat & 0x888; + uint16_t w2 = bitPat & 0x444; + uint16_t w1 = bitPat & 0x222; + uint16_t w0 = bitPat & 0x111;*/ + uint16_t w3 = bitPat & 0x88; + uint16_t w2 = bitPat & 0x44; + uint16_t w1 = bitPat & 0x22; + uint16_t w0 = bitPat & 0x11; + + uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF; + uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF; + uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF; + uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF; + + for(int8_t i=7; i>=0; i--) + { + uint8_t color = (((blurred0 >> i) & 0x01) << 3) + | (((blurred1 >> i) & 0x01) << 2) + | (((blurred2 >> i) & 0x01) << 1) + | ((blurred3 >> i) & 0x01); + blurTable[bitPat][7 - i] = color; + } + } +#else + for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10) + { + uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888; + + uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF; + uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF; + uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF; + uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF; + + for(int8_t i=7; i>=0; i--) + { + uint8_t color = (((blurred0 >> i) & 0x01) << 3) + | (((blurred1 >> i) & 0x01) << 2) + | (((blurred2 >> i) & 0x01) << 1) + | ((blurred3 >> i) & 0x01); + blurTable[bitPat >> 4][7 - i] = color; + } + } +#endif + + for(int i=0; i<256; i++) + { + mirrorTable[i] = ((i & 0x01) << 7) + | ((i & 0x02) << 5) + | ((i & 0x04) << 3) + | ((i & 0x08) << 1) + | ((i & 0x10) >> 1) + | ((i & 0x20) >> 3) + | ((i & 0x40) >> 5) + | ((i & 0x80) >> 7); + } +} + + +void TogglePalette(void) +{ + if (palette == (uint32_t *)colors) + { + palette = (uint32_t *)altColors; + SpawnMessage("Color TV palette"); + } + else + { + palette = (uint32_t *)colors; + SpawnMessage("\"Master Color Values\" palette"); + } +} + + +void CycleScreenTypes(void) +{ + char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" }; + + screenType++; + + if (screenType == ST_LAST_ENTRY) + screenType = ST_FIRST_ENTRY; + + SpawnMessage("%s", scrTypeStr[screenType]); +} + + +static uint32_t msgTicks = 0; +static char message[4096]; + +void SpawnMessage(const char * text, ...) +{ + va_list arg; + + va_start(arg, text); + vsprintf(message, text, arg); + va_end(arg); + + msgTicks = 120; +} + + +static void DrawString2(uint32_t x, uint32_t y, uint32_t color); +static void DrawString(void) +{ +//This approach works, and seems to be fast enough... Though it probably would +//be better to make the oversized font to match this one... + for(uint32_t x=7; x<=9; x++) + for(uint32_t y=7; y<=9; y++) + DrawString2(x, y, 0x00000000); + + DrawString2(8, 8, 0x0020FF20); +} + + +static void DrawString2(uint32_t x, uint32_t y, uint32_t color) +{ +//uint32_t x = 8, y = 8; + uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH); +// uint32_t color = 0x0020FF20; +//This could be done ahead of time, instead of on each pixel... +//(Now it is!) + uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF; + + for(uint32_t i=0; i> 16) & 0xFF, + eGreen = (existingColor >> 8) & 0xFF, + eRed = existingColor & 0xFF; + +//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries) +//Here we've modified it to have 33 levels of transparency (could have any # we want!) +//because dividing by 32 is faster than dividing by 31...! + uint8_t invTrans = 255 - trans; + + uint32_t bRed = (eRed * invTrans + nRed * trans) / 255; + uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255; + uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255; + +//THIS IS NOT ENDIAN SAFE +//NB: Setting the alpha channel here does nothing. + *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed; + } + } + } + + address += FONT_WIDTH; + } +} + + +static void Render40ColumnTextLine(uint8_t line) +{ + uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF); + + for(int x=0; x<40; x++) + { + uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x]; + + // Render character at (x, y) + + for(int cy=0; cy<8; cy++) + { + for(int cx=0; cx<7; cx++) + { + uint32_t pixel = 0xFF000000; + + if (alternateCharset) + { + if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)]) + pixel = pixelOn; + + if (chr < 0x80) + pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF); + + if ((chr & 0xC0) == 0x40 && flash) + pixel = 0xFF000000; + } + else + { + if (textChar2e[(chr * 56) + cx + (cy * 7)]) + pixel = pixelOn; + } + + scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel; + scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel; + + // QnD method to get blank alternate lines in text mode + if (screenType == ST_GREEN_MONO) + pixel = 0xFF000000; + + { + scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel; + scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel; + } + } + } + } +} + + +static void Render80ColumnTextLine(uint8_t line) +{ + uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF); + + for(int x=0; x<80; x++) + { +#if 0 +// This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0 + uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x]; + + if (x > 39) + chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40]; +#else + uint8_t chr; + + if (x & 0x01) + chr = ram[lineAddrLoRes[line] + (x >> 1)]; + else + chr = ram2[lineAddrLoRes[line] + (x >> 1)]; +#endif + + // Render character at (x, y) + + for(int cy=0; cy<8; cy++) + { + for(int cx=0; cx<7; cx++) + { + uint32_t pixel = 0xFF000000; + + if (alternateCharset) + { + if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)]) + pixel = pixelOn; + + if (chr < 0x80) + pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF); + + if ((chr & 0xC0) == 0x40 && flash) + pixel = 0xFF000000; + } + else + { + if (textChar2e[(chr * 56) + cx + (cy * 7)]) + pixel = pixelOn; + } + + scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel; + + // QnD method to get blank alternate lines in text mode + if (screenType == ST_GREEN_MONO) + pixel = 0xFF000000; + + scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel; + } + } + } +} + + +static void Render40ColumnText(void) +{ + for(uint8_t line=0; line<24; line++) + Render40ColumnTextLine(line); +} + + +static void Render80ColumnText(void) +{ + for(uint8_t line=0; line<24; line++) + Render80ColumnTextLine(line); +} + + +static void RenderLoRes(uint16_t toLine/*= 24*/) +{ +// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!! +// Also, we could set up three different Render functions depending on which +// render type was set and call it with a function pointer. Would be faster +// then the nested ifs we have now. +/* +Note that these colors correspond to the bit patterns generated by the numbers 0-F in order: +Color #s correspond to the bit patterns in reverse... Interesting! + +00 00 00 -> 0 [0000] -> 0 (lores color #) +3c 4d 00 -> 8 [0001] -> 8? BROWN +00 5d 3c -> 4 [0010] -> 4? DARK GREEN +3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN) +41 30 7d -> 2 [0100] -> 2? DARK BLUE +7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY +41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE) +7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA) +7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA) +ba 6d 41 -> 9 [1001] -> 9? ORANGE +7d 7d 7d -> 5 [1010] -> 5? DARK GRAY +ba cb 7d -> 13 [1011] -> 13? YELLOW +be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET) +fb 9e be -> 11 [1101] -> 11? PINK +be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN) +fb fb fb -> 15 [1111] -> 15 WHITE +*/ + uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; + +//This is the old "perfect monitor" rendering code... +/* if (screenType != ST_COLOR_TV) // Not correct, but for now... +//if (1) + { + for(uint16_t y=0; y> 4]; + + for(int cy=4; cy<8; cy++) + for(int cx=0; cx<14; cx++) + scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel; + } + } + } + else//*/ + + uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61); + + for(uint16_t y=0; y> 24; + pixels <<= 4; + + for(uint8_t j=0; j<4; j++) + { + uint8_t color = blurTable[bitPat][j]; + + for(uint32_t cy=0; cy<8; cy++) + { + scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; +// scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; + } + } + } + + previous3Bits = pixels & 0x70000000; + } + else + { + for(int j=0; j<28; j++) + { + for(uint32_t cy=0; cy<8; cy++) + { + scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); +// scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); + } + + pixels <<= 1; + } + } + } + + // Now do bottom half... + + previous3Bits = 0; + + for(uint16_t x=0; x<40; x+=2) + { + uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4; + uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4; + scrByte1 = mirrorNybble[scrByte1]; + scrByte2 = mirrorNybble[scrByte2]; + // This is just a guess, but it'll have to do for now... + uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16) + | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12) + | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2; + + // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF + // 0ppp 1111 1111 1111 11|11 1111 1111 1111 + // 31 27 23 19 15 11 7 3 0 + + if (screenType == ST_COLOR_TV) + { + for(uint8_t i=0; i<7; i++) + { + uint8_t bitPat = (pixels & 0x7F000000) >> 24; + pixels <<= 4; + + for(uint8_t j=0; j<4; j++) + { + uint8_t color = blurTable[bitPat][j]; + + for(uint32_t cy=8; cy<16; cy++) + { + scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; +// scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; + } + } + } + + previous3Bits = pixels & 0x70000000; + } + else + { + for(int j=0; j<28; j++) + { + for(uint32_t cy=8; cy<16; cy++) + { + scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); +// scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); + } + + pixels <<= 1; + } + } + } + } +} + + +static void RenderHiRes(uint16_t toLine/*= 192*/) +{ +//printf("RenderHiRes to line %u\n", toLine); +// NOTE: Not endian safe. !!! FIX !!! [DONE] +#if 0 + uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61); +#else +// Now it is. Now roll this fix into all the other places... !!! FIX !!! +// The colors are set in the 8-bit array as R G B A + uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF }; + uint32_t * colorPtr = (uint32_t *)monoColors; + uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]); +#endif + + for(uint16_t y=0; y> 24; + pixels <<= 4; + + for(uint8_t j=0; j<4; j++) + { + uint8_t color = blurTable[bitPat][j]; +#if 0 +//This doesn't seem to make things go any faster... +//It's the OpenGL render that's faster... Hmm... + scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color]; +#else + scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; + scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color]; +#endif + } + } + + previous3bits = pixels & 0x70000000; + } + else + { + for(int j=0; j<28; j++) + { + scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); + + if (screenType == ST_GREEN_MONO) + pixels &= 0x07FFFFFF; + + scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); + pixels <<= 1; + } + } + } + } +} + + +static void RenderDHiRes(uint16_t toLine/*= 192*/) +{ +// Now it is. Now roll this fix into all the other places... !!! FIX !!! +// The colors are set in the 8-bit array as R G B A + uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF }; + uint32_t * colorPtr = (uint32_t *)monoColors; + uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]); + + for(uint16_t y=0; y> 1); + + // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF + // 0ppp 1111 1111 1111 1111 1111 1111 1111 + // 31 27 23 19 15 11 7 3 0 + + if (screenType == ST_COLOR_TV) + { + for(uint8_t i=0; i<7; i++) + { + uint8_t bitPat = (pixels & 0xFE000000) >> 25; + pixels <<= 4; + + for(uint8_t j=0; j<4; j++) + { + uint32_t color = palette[blurTable[bitPat][j]]; + scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color; + scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color; + } + } + + previous4bits = pixels & 0xF0000000; + } + else + { + for(int j=0; j<28; j++) + { + scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); + + if (screenType == ST_GREEN_MONO) + pixels &= 0x07FFFFFF; + + scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000); + pixels <<= 1; + } + } + } + } +} + + +void RenderVideoFrame(void) +{ + if (GUI::powerOnState == true) + { + if (textMode) + { + if (!col80Mode) + Render40ColumnText(); + else + Render80ColumnText(); + } + else + { + if (mixedMode) + { + if (hiRes) + { + RenderHiRes(160); + Render40ColumnTextLine(20); + Render40ColumnTextLine(21); + Render40ColumnTextLine(22); + Render40ColumnTextLine(23); + } + else + { + RenderLoRes(20); + Render40ColumnTextLine(20); + Render40ColumnTextLine(21); + Render40ColumnTextLine(22); + Render40ColumnTextLine(23); + } + } + else + { + if (dhires) + RenderDHiRes(); + else if (hiRes) + RenderHiRes(); + else + RenderLoRes(); + } + } + } + else + { + memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t)); + } + + if (msgTicks) + { + DrawString(); + msgTicks--; + } +} // @@ -39,7 +944,8 @@ bool InitVideo(void) return false; } - int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer); +// int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer); + int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer); if (retVal != 0) { @@ -62,6 +968,8 @@ bool InitVideo(void) SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT); + SetupBlurTable(); + WriteLog("Video: Successfully initialized.\n"); return true; } @@ -73,19 +981,23 @@ bool InitVideo(void) void VideoDone(void) { WriteLog("Video: Shutting down SDL...\n"); + SDL_DestroyTexture(sdlTexture); + SDL_DestroyRenderer(sdlRenderer); + SDL_DestroyWindow(sdlWindow); SDL_Quit(); WriteLog("Video: Done.\n"); } // -// Render the screen buffer to the primary screen surface +// Render the Apple video screen to the primary texture // -void RenderScreenBuffer(void) +void RenderAppleScreen(SDL_Renderer * renderer) { - SDL_UpdateTexture(sdlTexture, NULL, scrBuffer, VIRTUAL_SCREEN_WIDTH * sizeof(Uint32)); - SDL_RenderClear(sdlRenderer); - SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); + SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch); + RenderVideoFrame(); + SDL_UnlockTexture(sdlTexture); + SDL_RenderCopy(renderer, sdlTexture, NULL, NULL); } @@ -103,4 +1015,3 @@ void ToggleFullScreen(void) WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError()); } - diff --git a/src/video.h b/src/video.h index bd510d5..3adff99 100644 --- a/src/video.h +++ b/src/video.h @@ -1,12 +1,11 @@ // -// VIDEO.H: Header file +// Apple 2/host video support // #ifndef __VIDEO_H__ #define __VIDEO_H__ #include -#include // For uint32_t // These are double the normal width because we use sub-pixel rendering. //#define VIRTUAL_SCREEN_WIDTH 280 @@ -14,16 +13,32 @@ //#define VIRTUAL_SCREEN_HEIGHT 192 #define VIRTUAL_SCREEN_HEIGHT 384 +// Global variables (exported) + +extern bool flash; +extern bool textMode; +extern bool mixedMode; +extern bool displayPage2; +extern bool hiRes; +extern bool alternateCharset; +extern bool col80Mode; +extern SDL_Renderer * sdlRenderer; + +// Functions (exported) + +//void SetupBlurTable(void); +void TogglePalette(void); +void CycleScreenTypes(void); +void SpawnMessage(const char * text, ...); bool InitVideo(void); void VideoDone(void); -void RenderScreenBuffer(void); +void RenderAppleScreen(SDL_Renderer *); void ToggleFullScreen(void); + // Exported crap -extern SDL_Renderer * sdlRenderer; -extern uint32_t scrBuffer[]; -extern uint32_t mainScrBuffer[]; +//extern uint32_t * scrBuffer; +//extern int scrPitch; #endif // __VIDEO_H__ -